环境初始化
1. npm init -y
2.在当前根目录下新建 src 目录和 scripts 目录,在 src 新建 index.js、在 scripts 里面新建 rollup.config.js 如下
|-- src
|--index.js
|--scripts
| --rollup.config.js
|-- package.json
配置 rollup.config.js 如 babel
1.使用 Babel 和 Rollup 的最简单方法是使用 @rollup/plugin-babel。 首先,安装插件
npm i -D @rollup/plugin-babel @rollup/plugin-node-resolve
2.然后在 rollup.config.js 添加如下配置
import resolve from '@rollup/plugin-node-resolve'
import babel from '@rollup/plugin-babel'
export default {
input: 'src/index.js',
output: {
file: 'bundle.js',
format: 'cjs'
},
plugins: [
resolve(),
babel({
babelHelpers: 'bundled',
exclude: 'node_modules/**' // 只编译我们的源代码
})
]
}
3.安装 babel-core and the env preset,同时在根目录下新建.babelrc 进行配置
npm i -D @babel/core @babel/preset-env
{
"presets": [
["@babel/preset-env", {
"modules": false
}]
]
}
配置 npm scripts
1.执行 rollup,看看输出结果
npx rollup -c './scripts/rollup.config.js'
执行完会在当前根目录下输出一个 bundle.js,如果想实时更改编译输出执行
npx rollup -w -c './scripts/rollup.config.js'
2.修改 rollup.config.js 的输出格式
output: [
{
file: './lib/index.iife.js',
name: 'taorem',
format: 'iife'
},
{
file: './lib/index.umd.js',
name: 'taorem',
format: 'umd'
},
{
file: './lib/index.esm.js',
format: 'esm'
}
]
3.在 package.json 配置 scripts
"scripts": {
"build": "rollup -c './scripts/rollup.config.js'",
"build:watch": "rollup -w -c './scripts/rollup.config.js'"
},
执行 npm run build,此时会报错 sh: rollup: command not found,这是因为 rollup 还没有安装,一般我们不会安装在本地全局
npm i -D rollup
再次执行npm run build,输出如下
本地测试打包结果
1.在根目录新建 examples 文件夹,里面建一个 index.html,引入我们的打包过后文件夹的文件。
<script src="../lib/index.iife.js"></script>
2.我们本地需要启动一个服务,可用rollup-plugin-serve或者browser-sync,常用插件可以从这里了解。这里我们用 browser-sync
npm install -D browser-sync
3.配置 Browsersync options
npx browser-sync init
执行完会在根目录下创建一个 bs-config.js,内容大概如下
/*
|--------------------------------------------------------------------------
| Browser-sync config file
|--------------------------------------------------------------------------
|
| For up-to-date information about the options:
| http://www.browsersync.io/docs/options/
|
| There are more options than you see here, these are just the ones that are
| set internally. See the website for more info.
|
|
*/
module.exports = {
ui: {
port: 3001
},
files: false,
watchEvents: ['change'],
watch: false,
ignore: [],
single: false,
watchOptions: {
ignoreInitial: true
},
server: false,
proxy: false,
port: 3000,
middleware: false,
serveStatic: [],
ghostMode: {
clicks: true,
scroll: true,
location: true,
forms: {
submit: true,
inputs: true,
toggles: true
}
},
logLevel: 'info',
logPrefix: 'Browsersync',
logConnections: false,
logFileChanges: true,
logSnippet: true,
rewriteRules: [],
open: 'local',
browser: 'default',
cors: false,
xip: false,
hostnameSuffix: false,
reloadOnRestart: false,
notify: true,
scrollProportionally: true,
scrollThrottle: 0,
scrollRestoreTechnique: 'window.name',
scrollElements: [],
scrollElementMapping: [],
reloadDelay: 0,
reloadDebounce: 500,
reloadThrottle: 0,
plugins: [],
injectChanges: true,
startPath: null,
minify: true,
host: null,
localOnly: false,
codeSync: true,
timestamps: true,
clientEvents: [
'scroll',
'scroll:element',
'input:text',
'input:toggles',
'form:submit',
'form:reset',
'click'
],
socket: {
socketIoOptions: {
log: false
},
socketIoClientConfig: {
reconnectionAttempts: 50
},
path: '/browser-sync/socket.io',
clientPath: '/browser-sync',
namespace: '/browser-sync',
clients: {
heartbeatTimeout: 5000
}
},
tagNames: {
less: 'link',
scss: 'link',
css: 'link',
jpg: 'img',
jpeg: 'img',
png: 'img',
svg: 'img',
gif: 'img',
js: 'script'
},
injectNotification: false
}
将此文件移动到 scripts 文件夹,加入 const path = require(‘path’)
修改 options 的 files 为
const rootDir = path.resolve(‘../‘)files: [path.join(rootDir, ‘./lib’), path.join(rootDir, ‘./examples’)]
修改 serveStatic 为 serveStatic: [path.join(rootDir, ‘./lib’), path.join(rootDir, ‘./examples’)]
server 为server: true
startPath 为 startPath: ‘./examples/index.html’
完整的如下
const path = require('path')
const rootDir = path.resolve('../')
/*
|--------------------------------------------------------------------------
| Browser-sync config file
|--------------------------------------------------------------------------
|
| For up-to-date information about the options:
| http://www.browsersync.io/docs/options/
|
| There are more options than you see here, these are just the ones that are
| set internally. See the website for more info.
|
|
*/
module.exports = {
ui: {
port: 3001
},
files: [path.join(rootDir, './lib'), path.join(rootDir, './examples')],
watchEvents: ['change'],
watch: false,
ignore: [],
single: false,
watchOptions: {
ignoreInitial: true
},
server: true,
proxy: false,
port: 3000,
middleware: false,
serveStatic: [path.join(rootDir, './lib'), path.join(rootDir, './examples')],
ghostMode: {
clicks: true,
scroll: true,
location: true,
forms: {
submit: true,
inputs: true,
toggles: true
}
},
logLevel: 'info',
logPrefix: 'Browsersync',
logConnections: false,
logFileChanges: true,
logSnippet: true,
rewriteRules: [],
open: 'local',
browser: 'default',
cors: false,
xip: false,
hostnameSuffix: false,
reloadOnRestart: false,
notify: true,
scrollProportionally: true,
scrollThrottle: 0,
scrollRestoreTechnique: 'window.name',
scrollElements: [],
scrollElementMapping: [],
reloadDelay: 0,
reloadDebounce: 500,
reloadThrottle: 0,
plugins: [],
injectChanges: true,
startPath: './examples/index.html',
minify: true,
host: null,
localOnly: false,
codeSync: true,
timestamps: true,
clientEvents: [
'scroll',
'scroll:element',
'input:text',
'input:toggles',
'form:submit',
'form:reset',
'click'
],
socket: {
socketIoOptions: {
log: false
},
socketIoClientConfig: {
reconnectionAttempts: 50
},
path: '/browser-sync/socket.io',
clientPath: '/browser-sync',
namespace: '/browser-sync',
clients: {
heartbeatTimeout: 5000
}
},
tagNames: {
less: 'link',
scss: 'link',
css: 'link',
jpg: 'img',
jpeg: 'img',
png: 'img',
svg: 'img',
gif: 'img',
js: 'script'
},
injectNotification: false
}
在 package.json 里面配置 scripts
"scripts": {
"server": "browser-sync start -c './scripts/bs-config.js'",
},
为了方便调试我们再增加两个 scripts
"start": "npm run server",
"dev": "npm run build:watch & npm run server"
开始编写 rem 代码
将 html
中的 font-size
作为一个视觉与实际显示宽度作为缩放因子,并乘以 100
(为提供精度,部分浏览器 font-size 忽略小数点);转换公式如下:
rootFontSize = (designWidth / viewportWidth) * 100
remValue = designX / 100
const win = window
const doc = document
const taorem = (options = {}) => {
const normalFontSize = 14
const { designWidth = 750, maxClientWidth = 1024, bodyFontSize = normalFontSize } = options
const docEl = doc.documentElement
const dpr = win.devicePixelRatio || 1
const headEl = doc.head
let rem = 16
// adjust body font size
function setBodyFontSize() {
const bodyEl = doc.body
if (bodyEl) {
bodyEl.style.fontSize = `${bodyFontSize}px`
}
}
// set 1rem = viewWidth / 10
function setRemUnit() {
rem = (Math.min(docEl.clientWidth, maxClientWidth) / designWidth) * 100
docEl.style.fontSize = `${rem}px`
/* 针对系统自定义字号导致页面错乱问题修复 */
const styles = window.getComputedStyle(docEl)
const fontSize = parseFloat(styles.fontSize)
// rem单位进行四舍五入保留4位小数再比较,因为系统仅保留4位
if (fontSize !== Math.round(rem * 10000) / 10000) {
rem = rem * (rem / fontSize) // 根据实际fontSize尺寸得出缩放比例,计算出页面需要的fontSize
docEl.style.fontSize = `${rem}px`
}
}
setRemUnit()
// reset rem unit on page resize
window.addEventListener('resize', setRemUnit)
window.addEventListener('pageshow', function (e) {
if (e.persisted) {
setRemUnit()
}
})
if (doc.body) {
setBodyFontSize()
} else {
doc.addEventListener('DOMContentLoaded', setBodyFontSize)
}
return {
rem,
dpr,
setRemUnit,
rem2px: (d) => {
const val = parseFloat(d) * rem
if (/^([0-9.]+)rem$/.test(d)) {
return `${val}px`
}
return val
},
px2rem: (d) => {
const val = parseFloat(d) / rem
if (/^([0-9]+)px$/.test(d)) {
return `${val}rem`
}
return val
}
}
}
const scripts = doc.scripts
const currentScript = scripts[scripts.length - 1]
const taoremEnable = currentScript && currentScript.getAttribute('taorem')
if (taoremEnable !== null || taoremEnable !== undefined) {
const dataset = { ...currentScript.dataset }
taorem(dataset)
}
export default taorem
examples index.html 如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>测试</title>
<script taorem src="../lib/index.iife.js"></script>
</head>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
width: 100%;
height: 100%;
}
.container {
display: block;
width: 100%;
height: 5rem;
overflow: hidden;
}
.aside {
display: block;
float: left;
width: 2rem;
height: 5rem;
background: yellow;
}
.content {
display: block;
overflow: hidden;
height: 5rem;
background: red;
}
.hairline {
margin: 1rem auto;
border: #fff 0.5px solid;
}
</style>
<body>
<h1>taorem测试方案</h1>
<div class="container">
<aside class="aside"></aside>
<content class="content">
<div class="hairline"></div>
</content>
</div>
</body>
<script></script>
</html>
发布到 npm
在 package.json 添加配置
"main": "lib/index.umd.js",
"module": "lib/index.esm.js",
"browser": "lib/index.iife.js",
"files": [
"src",
"lib",
"examples"
],
发布到 npm,注意需要将包名改下,不然发布会报错
npm login
npm publish
添加 eslint
代码添加 eslint,rollup-plugin-eslint
npm install eslint rollup-plugin-eslint --save-dev
在 rollup.config.js 添加配置
import { eslint } from 'rollup-plugin-eslint'
plugins: [eslint()]
然后建立.eslintrc.js 来根据自己风格配置具体检测
module.exports = {
env: {
browser: true,
es6: true,
node: true
},
extends: 'eslint:recommended',
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module'
}
}
添加发布信息
在 rollup.config.js 添加
import dayjs from 'dayjs'
import pkg from '../package.json'
const banner = `/**
* REM布局方案
* @version: ${pkg.version}
* @author: wutao
* @date: ${dayjs().format('YYYY/MM/DD')}
**/`
output: [
{
file: './lib/index.iife.js',
name: 'taorem',
format: 'iife',
banner
},
{
file: './lib/index.umd.js',
name: 'taorem',
format: 'umd',
banner
},
{
file: './lib/index.esm.js',
format: 'esm',
banner
}
],
//同时安装依赖
npm install -D dayjs