-
Webpack DefinePlugin
const webpack = require('webpack') module.exports = { mode: 'none', entry: './src/main.js', output: { filename: 'bundle.js' }, plugins: [ new webpack.DefinePlugin({ // 值要求的是一个代码片段 API_BASE_URL: JSON.stringify('https://api.example.com') }) ]} -
模Webpack 体验和使用 Tree Shaking
通常用于描述移除 JavaScript 上下文中的未引用代码 Tree shaking 不是指某个配置选项,它是一组共能搭配使用后的优化效果,生产模式下自动开启 webpack.config.js module.exports = { mode: 'none', entry: './src/index.js', output: { filename: 'bundle.js' }, optimization: { // 模块只导出被使用的成员 usedExports: true, // 尽可能合并每一个模块到一个函数中,既提升了运行效率,又减少了代码的体积 concatenateModules: true, // 压缩输出结果 // minimize: true } } -
Webpack Tree Shaking 与 Babel
webpack.config.js 很多版本使用babel就会使tree shaking 失效, tree shaking 前提是有ES Modulkes module.exports = { mode: 'none', entry: './src/index.js', output: { filename: 'bundle.js' }, module: { rules: [ { test: /\.js$/, use: { loader: 'babel-loader', options: { presets: [ // 如果 Babel 加载模块时已经转换了 ESM,则会导致 Tree Shaking 失效 // ['@babel/preset-env', { modules: 'commonjs' }] // ['@babel/preset-env', { modules: false }] // 也可以使用默认配置,也就是 auto,这样 babel-loader 会自动关闭 ESM 转换 ['@babel/preset-env', { modules: 'auto' }] ] } } } ] }, optimization: { // 模块只导出被使用的成员 usedExports: true, // 尽可能合并每一个模块到一个函数中 // concatenateModules: true, // 压缩输出结果 // minimize: true } } -
Webpack sideEffects 和注意事项
sideEffects 副作用 : 模块执行时除了导出成员之外所作的事情 sideEffects一般用于npm 包标记是否有副作用 module.exports = { mode: 'none', entry: './src/index.js', output: { filename: 'bundle.js' }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] } ] }, optimization: { sideEffects: true, // 模块只导出被使用的成员 // usedExports: true, // 尽可能合并每一个模块到一个函数中 // concatenateModules: true, // 压缩输出结果 // minimize: true, } } 确定代码真的没有副作用 { "name": "31-side-effects", "version": "0.1.0", "main": "index.js", "author": "zce <w@zce.me> (https://zce.me)", "license": "MIT", "scripts": { "build": "webpack" }, "devDependencies": { "css-loader": "^3.2.0", "style-loader": "^1.0.0", "webpack": "^4.41.2", "webpack-cli": "^3.3.9" }, "sideEffects": [ "./src/extend.js", // extend.js 里有副作用的代码也打包进来 "*.css" ]} -
Webpack 代码分割
代码过多,打包后打包到一起会显得包特别大,所以并不是每个模块在启动时都是必要的,分包,按需加载 提高运行效率 code splitting: 代码分包 1. 多入口打包 2. 动态导入 -
Webpack 多入口打包
最常用的打包规则: 一个页面对应一个打包入口,公共部门单独提取 例子: 多个页面index.html,inedex.js 和album.html, album.js const { CleanWebpackPlugin } = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { mode: 'none', entry: { // 设置多入口 index: './src/index.js', album: './src/album.js' }, output: { filename: '[name].bundle.js' // [name] 就会替换成入口设置的名字 }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] } ] }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'Multi Entry', template: './src/index.html', filename: 'index.html', chunks: ['index'] // 使每个打包的html的只引用自己需要使用的js文件 }), new HtmlWebpackPlugin({ title: 'Multi Entry', template: './src/album.html', filename: 'album.html', chunks: ['album'] }) ]} -
Webpack 提取公共模块
不同入口中肯定会有公共模块 const { CleanWebpackPlugin } = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { mode: 'none', entry: { index: './src/index.js', album: './src/album.js' }, output: { filename: '[name].bundle.js' }, optimization: { splitChunks: { // 自动提取所有公共模块到单独 bundle chunks: 'all' } }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] } ] }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'Multi Entry', template: './src/index.html', filename: 'index.html', chunks: ['index'] }), new HtmlWebpackPlugin({ title: 'Multi Entry', template: './src/album.html', filename: 'album.html', chunks: ['album'] }) ]} -
Webpack 动态导入和魔法注释
需要用到某个模块时,在加载这个模块,动态导入的模块会被自动分包 index.js 入口文件 // import posts from './posts/posts' // import album from './album/album' const render = () => { const hash = window.location.hash || '#posts' const mainElement = document.querySelector('.main') mainElement.innerHTML = '' if (hash === '#posts') { // mainElement.appendChild(posts()) // /* webpackChunkName: 'components' */ 这就可以给分包起一个名字 import(/* webpackChunkName: 'components' */'./posts/posts').then(({ default: posts }) => { mainElement.appendChild(posts()) }) } else if (hash === '#album') { // mainElement.appendChild(album()) import(/* webpackChunkName: 'components' */'./album/album').then(({ default: album }) => { mainElement.appendChild(album()) }) }}render()window.addEventListener('hashchange', render) -
Webpack 提取css到单个文件MiniCssExtractPlugin和压缩文件OptimizeCssAssetsWebpackPlugin
提取css到单个文件 const { CleanWebpackPlugin } = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') // yarn add mini... // 压缩输出的css文件 yarn add optimize-css-assets-webpack-pluginconst OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin') // 压缩js的插件 const TerserWebpackPlugin = require('terser-webpack-plugin') module.exports = { mode: 'none', entry: { main: './src/index.js' }, output: { filename: '[name].bundle.js' }, optimization: { minimizer: [ // 压缩类的属性应该配置到minimizer里,进行统一控制;而不是在plugins里 new TerserWebpackPlugin(), // 压缩文件js不会被压缩,需要使用js的压缩配置进行压缩 new OptimizeCssAssetsWebpackPlugin() // 压缩 ] }, module: { rules: [ { test: /\.css$/, use: [ // 'style-loader', // 将样式通过 style 标签注入页面 MiniCssExtractPlugin.loader, // 会单独存放在css文件中,通过link方式引入 'css-loader' ] } ] }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'Dynamic import', template: './src/index.html', filename: 'index.html' }), new MiniCssExtractPlugin() // 添加安装的mini-css..的包 ]} -
Webpack 输出文件名 Hash
substitutions 建议:生产模式下,文件名使用hash const { CleanWebpackPlugin } = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin') const TerserWebpackPlugin = require('terser-webpack-plugin') module.exports = { mode: 'none', entry: { main: './src/index.js' }, output: { filename: '[name]-[contenthash:8].bundle.js' }, optimization: { minimizer: [ new TerserWebpackPlugin(), new OptimizeCssAssetsWebpackPlugin() ] }, module: { rules: [ { test: /.css$/, use: [ // 'style-loader', // 将样式通过 style 标签注入 MiniCssExtractPlugin.loader, 'css-loader' ] } ] }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'Dynamic import', template: './src/index.html', filename: 'index.html' }), new MiniCssExtractPlugin({ // filename: '[name]-[hash].bundle.css' 项目级别的hash,任何地方修改,hash值都会变化 // filename: '[name]-[chunkhash].bundle.css' 只要是同一路的打包,名称都是相同的 filename: '[name]-[contenthash:8].bundle.css' //文件级别的,:8 指定hash的字符长度 }) ]}
三、其他打包工具
-
Rollup 概述
es Module 打包器的一种,细小的代码打包为整块的代码 rollup 与webpack 作用类似, rollup 更为小巧 rollup 中并不支持类似HMR这种高级特性 提供一个充分利用ESM各项特性的高校打包器 -
Rollup 快速上手
yarn init yarn add rollup yarn rollup // 直接运行 会打印出所有帮助信息 yarn rollup ./src/index.js --format iife //--format设置输出的设置; iife 自调用函数的格式 // 此时的打包结果会显示在控制台中,下面这一步让其打包到dist文件里 yarn rollup ./src/index.js --format iife --file dist/bundle.js -
Rollup 配置文件和配置插件
// 设置配置文件,要放在根目录下 rollup.config.js 配置文件 import json from 'rollup-plugin-json' // yarn add rollup-plugin-jsonimport resolve from 'rollup-plugin-node-resolve' // yarn add rollup-plugin-node-resolveimport commonjs from 'rollup-plugin-commonjs'export default { input: 'src/index.js', // 指定打包的入口文件路径 output: { // 输出的相关配置 file: 'dist/bundle.js', // 输出的文件名 format: 'iife' // 输出的格式 }, plugins: [ // 使用插件 json(),//需要引用json文件的js里通过import{name}from '../xxx.json'方式直接引用json里参数 resolve(), commonjs() ]} src/index.js 入口文件 // 导入模块成员 // 使用esm的方式而不是直接使用lodash,是因为rollup默认只能够处理esm,如果直接导入commonjs模块 //是不被支持的,需要先安装yarn add rollup-plugin-commonjs 后才能使用commonjs相关的模块import _ from 'lodash-es' import { log } from './logger'import messages from './messages'import { name, version } from '../package.json'import cjs from './cjs-module' // => module.exports = {foo: 'bar'}// 使用模块成员const msg = messages.hilog(msg)log(name)log(version)log(_.camelCase('hello world'))log(cjs) // 1. yarn rollup --config要使用--config 运行,这样才能使用config文件里的配置 // 2. rollup自身的内容就只是js模块的合并打包,如要额外的需求,则需要使用插件 // 插件是rollup唯一的扩展方式 // 3. rollup默认只能够按照文件路径的方式加载本地的文件模块,使用rollup-plugin-node-resolve // 就可以通过模块名称导入对应的模块,列如上面index.js文件 导入loadsh-es模块 -
Rollup 代码拆分
动态导入的方式 src/index.js // 入口文件 import('./logger').then(({ log }) => { log('code splitting~')} ) // 配置文件 export default { input: 'src/index.js', output: { // file: 'dist/bundle.js', // 拆分需要输出多个文件,就不能只打包到一个js文件里 // format: 'iife' // 使用代码拆分的方式,就不能使用iife的输出格式,因为它是把代码合一起的 dir: 'dist', // 输入多个文件使用dir方式 format: 'amd' // 使用amd的格式可以输出打包结果 }} -
Rollup 多入口打包
rollup.config.js export default { // input: ['src/index.js', 'src/album.js'], input: { // 多入口打包 foo: 'src/index.js', bar: 'src/album.js' }, output: { dir: 'dist', format: 'amd' }} index.js import fetchApi from './fetch' import { log } from './logger' fetchApi('/posts').then(data => { data.forEach(item => { log(item) }) }) // 支持多入口打包,对于不同文件公共的部分也会提取到单个文件中,作为单个bundle // 对于amd输出格式的,不能直接引用到页面上,需要通过引用adm的库去加载 <!-- AMD 标准格式的输出 bundle 不能直接引用 --> <!-- <script src="foo.js"></script> --> <!-- 需要 Require.js 这样的库 --> <script src="https://unpkg.com/requirejs@2.3.6/require.js" data-main="foo.js"></script> -
Rollup 选用原则
Rollup 优点: 1. 输出结果更加扁平 2. 自动移除未引用代码 3. 打包结果依然完全可读 缺点: 1. 加载非ESM得第三方模块比较复杂 2. 模块最终都被打包到一个函数中,无法实现HMR 3. 浏览器环境中,代码拆分功能依赖AMD库 开发大的应用程序,缺点会比较明显 开发一个javascript框架或者类库,优点就比较明显 -
Parcel
零配置的前端应用打包器, 优点:多进程同时工作,构建速度快 1. yarn init 2. yarn add parcel-bundler --dev 安装parcel //谐音:派so 3. 新建 src/index.html //这将是后面打包的入口文件 // 官方推荐,因为它是应用运行在浏览器端的入口文件index.html index.html <body> <script src="main.js"></script></body> main.js // 直接引入会自动安装依赖 // import $ from 'jquery' import foo from './foo' import './style.css' import logo from './zce.png'foo.bar() import('jquery').then($ => { // 动态导入的方式 $(document.body).append('<h1>Hello Parcel</h1>') $(document.body).append(`<img src="${logo}" />`)}) if (module.hot) { // 热更新,当前模块或者当前模块依赖的文件有修改,才会自动更新 module.hot.accept(() => { console.log('hmr') }) } yarn parcel src/index.html // 运行开发模式打包 yarn parcel build src/index.html // 以生产方式进行打包
四、规范化标准
-
规范化介绍
-
ESLint 介绍
最为主流的jslint工具监测js代码质量 容易统一开发者的编码风格 帮助开发者提升编码能力 -
ESLint 安装
1. 初始化项目 npm init 2. 安装ESlint 模块为开发依赖 npm install eslint --save-dev 3. cli命令验证安装结果cd .\node_modules\ ,cd .\.bin\, .\eslint 或者npx eslint都能查看 -
ESLint 快速上手
npx eslint --init 一、会有三个选项 1. To check syntax only // 只检查语法错误 2. To check syntax and find problems // 检查语法并发现问题 // 3. 检查语法、发现问题并强制执行代码样式3. To check syntax, find problems, and enforce code style 二、模块化采用哪种方式 三、用的是哪个框架 四、是否用到typescript 五、运行在哪个环境中 六、怎样定义代码风格 七、配置文件以何种格式存放/ js 八、会问是否安装插件/ 安装 npx eslint .\01.js --fix -
ESLint 配置文件解析
.eslintrc.js module.exports = { env: { // 标记代码最终的运行环境,根据环境信息判断全局成员是否是可用的 browser: true, // 代码运行在浏览器中 es2020: true }, extends: [ // 集成一些共享配置 'standard' ], parserOptions: { ecmaVersion: 11 // 控制es6使用的版本 }, rules: { // 配置效验规则的开启和关闭 'no-alert': "error" // off 关闭,on 开启 ,error 报出错误// 使用 alert()会报错 }globals: { // 代码中可以使用的全局成员 "jQuery": "readonly" }} -
ESLint 配置注释
将配置通过注释的方式写在脚本文件中,然后执行代码效验 1. const str1 = "${name} is a coder" // eslint-disable-line no-template-curly-in-string 2. console.log(str1)这样就可以禁用第一行的eslint校验 -
ESLint 结合自动化工具
优点: 集成之后,eslint 一定会工作; 与项目统一,管理更加方便 -
ESLint 结合 Webpack 配置
-
现代化项目集成 ESlint
vue create wx-vue-app // 创建vue项目 -
ESLint 检查 TypeScript
.eslintrc.js
module.exports = { env: { browser: true, es2020: true }, extends: [ 'standard' ], parser: '@typescript-eslint/parser', // 语法解析器 parserOptions: { ecmaVersion: 11 }, plugins: [ '@typescript-eslint' ], rules: { }}
-
Stylelint 认识
提供默认的代码检查规则 提供cli工具,快速调用 通过插件支持sass less postcss 支持gulp 或者webpack集成 安装 npm install stylelint -D npx stylelint ./index.css 快速找到stylelint 创建 .stylelintrc.js
.stylelintrc.js
module.exports = { // 如果有多个,可以使用数组 extends: 'stylelint-config-standard' }
-
Prettier 的使用
通用前端代码格式化工具
npm install perttier -D
npx prettier style.css --write //将对应的css代码给直接格式化 npx prettier . --write // 将所有文件代码格式化
-
Git Hooks 工作机制
通过git Hooks在代码提交前强制lint 介绍: 1. git Hook 也称之为git钩子,每个钩子都对应一个任务 2. 通过shell 脚本可以编写钩子任务触发时要具体执行的操作
在本地git文件中 hooks文件中找到 perpare-commit.sample的文件进行修改
-
ESLint 结合 Git Hooks
Husky 可以实现git Hooks的使用需求 安装 npm install husky -D
padkage.json里添加和修改配置 "scripts": { "test": "eslint ./index.js" }
"husky":{ "hooks": { "pre-commit": "npm run test" } }
git add . git commit -m '测试' // 代码就会lint操作
- lint-staged // commit之后可以继续检查格式化
2.1 安装 npm install lint-staged -d 2.2 配置具体配置
package.json里配置 "scripts": { "test": "eslint ./index.js", "percommit": "lint-staged" }"husky":{ "hooks": { "pre-commit": "npm run percommit" } } "lint-staged": { "*.js": { "eslint", "git add" } }
git add . git commit -m 'zzz' //就会强行lint