分支
- master 最开始的主干,多页面配置
- large-number webpack 构建包和组件上传到 npm
- ssr_branch SSR 功能,有利于 SEO,请求减少
- webpack-gongnengsheji
-
webpack_advanced_one 进阶
-
安装 编译解析 ES6
- 1.
npm i @babel/core @babel/preset-env babel-loader -D
- 2.建立.babelrc
- 1.
-
解析 React JSX
-
npm i react react-dom @babel/preset-react -D
-
- 建立 src/search.js 写入 react
-
-
解析 CSS
-
npm i style-loader css-loader -D
-
- css-loader 用于加载.css 文件,并且转换成 commonjs 对象;style-loader 将样式通过
<style>
标签插入到 head 中
- css-loader 用于加载.css 文件,并且转换成 commonjs 对象;style-loader 将样式通过
-
-
解析 Less 和 Sass
- 1.less-loader 用于将 less 转换成 css
npm i less less-loader -D
- 2.sass-loader 用于将 sass 转换成 css
- 1.less-loader 用于将 less 转换成 css
-
解析图片
- 1.
npm i file-loader -D
- 1.
-
解析字体
- 1.
npm i file-loader -D
- 1.
-
资源解析
npm i url-loader -D
- 也可以处理图片和字体
- 可以设置较小资源自动 base64
-
webpack 中文件监听 两种方法
-
webpack 中的热更新及原理
-
将 css 打包成单独的文件
npm i mini-css-extract-plugin -D
-
文件指纹
-
代码压缩
-
自动清理构建目录产物
- 通过 npm scripts 清理构建目录
rm -rf ./dist && webpack
orrimraf ./dist && webpack
- 使用 clean-webpack-plugin 默认会删除 output 指定的输出目录
npm i clean-webpack-plugin -D
- 通过 npm scripts 清理构建目录
-
PostCSS 插件
autoprefixer
自动补齐 CSS3 前缀npm i postcss-loader autoprefixer -D
- 使用
autoprefixer
插件,根据 Can I Use 规则(https://caniuse.com/)
-
移动端 CSS px 自动转换成 rem
-
静态资源内联
-
多页面应用(MPA)概念
每一次页面跳转的时候,后台服务器都会给返回一个新的 html 文档,这种类型的网站也就是多页网站,也叫做多页应用
多页面打包基本思路
每个页面对应一个 entry,一个 html-webpack-plugin
缺点 : 每次新增或删除页面需要改 webpack 配置
多页面打包通用方案
动态获取 entry 和设置 html-webpack-plugin 数量
利用 glob.sync glob
entry:glob.sync(path.join(__dirname,'./src/*/index.js'))
npm i glob -D
/** * 动态的获取entry */ const setMPA = () => { const entry = {}; const htmlWebpackPlugins = []; const entryFiles = glob.sync(path.join(__dirname, "./src/*/index.js")); Object.keys(entryFiles).map(index => { const entryFile = entryFiles[index]; //'/Users/fangcao/Documents/study/study_webpack/src/index/index.js' const match = entryFile.match(/src\/(.*)\/index\.js/); const pageName = match && match[1]; entry[pageName] = entryFile; htmlWebpackPlugins.push( new HtmlWebpackPlugin({ template: path.join( __dirname, `src/${pageName}/index.html` ), filename: `${pageName}.html`, chunks: [pageName], inject: true, minify: { html5: true, collapseWhitespace: true, preserveLineBreaks: false, minifyCSS: true, minifyJS: true, removeComments: false } }) ); // console.log("pageName", match, match[1], pageName, entry); }); // console.log("entryFiles", entryFiles); return { entry, htmlWebpackPlugins }; }; const { entry, htmlWebpackPlugins } = setMPA(); module.exports = { ... entry: entry, ... plugins: [ ... ].concat(htmlWebpackPlugins) ... }
-
使用 source map
作用:通过 source map 定位到源代码 source map 科普文:http://www.ruanyifeng.com/blog/2013/01/javascript_source_map.html
开发环境开启,线上环境关闭
线上排查问题的时候可以将 sourcemap 上传到错误监控系统
// webpack.config.js里面 devtool: "source-map";
-
基础库分离
##分离基础包方法一
`bash npm i html-webpack-externals-plugin -D` ```javascript //webpack.config.js文件里加一下代码: const HtmlWebpackExternalsPlugin = require("html-webpack-externals-plugin"); new HtmlWebpackExternalsPlugin({ externals: [ { module: "react", entry: "https://unpkg.com/react@16.9.0/umd/react.production.min.js", global: "React" }, { module: "react-dom", entry: "https://unpkg.com/react-dom@16.9.0/umd/react-dom.production.min.js", global: "ReactDOM" } ] }); //对应的页面加上 <script crossorigin src="https://unpkg.com/react@16.9.0/umd/react.production.min.js" ></script> <script crossorigin src="https://unpkg.com/react-dom@16.9.0/umd/react-dom.production.min.js" ></script> ```
##分离基础包方法二
/** * 动态的获取entry */ const setMPA = () => { const entry = {}; const htmlWebpackPlugins = []; const entryFiles = glob.sync(path.join(__dirname, "./src/*/index.js")); Object.keys(entryFiles).map(index => { const entryFile = entryFiles[index]; //'/Users/fangcao/Documents/study/study_webpack/src/index/index.js' const match = entryFile.match(/src\/(.*)\/index\.js/); const pageName = match && match[1]; entry[pageName] = entryFile; htmlWebpackPlugins.push( new HtmlWebpackPlugin({ template: path.join( __dirname, `src/${pageName}/index.html` ), filename: `${pageName}.html`, chunks: ["vendors", pageName], inject: true, minify: { html5: true, collapseWhitespace: true, preserveLineBreaks: false, minifyCSS: true, minifyJS: true, removeComments: false } }) ); // console.log("pageName", match, match[1], pageName, entry); }); // console.log("entryFiles", entryFiles); return { entry, htmlWebpackPlugins }; }; optimization: { splitChunks: { cacheGroups: { commons: { test: /(react|react-dom)/, name: "vendors", chunks: "all" } } } }
##分离页面公共文件
/** * 动态的获取entry */ const setMPA = () => { const entry = {}; const htmlWebpackPlugins = []; const entryFiles = glob.sync(path.join(__dirname, "./src/*/index.js")); Object.keys(entryFiles).map(index => { const entryFile = entryFiles[index]; //'/Users/fangcao/Documents/study/study_webpack/src/index/index.js' const match = entryFile.match(/src\/(.*)\/index\.js/); const pageName = match && match[1]; entry[pageName] = entryFile; htmlWebpackPlugins.push( new HtmlWebpackPlugin({ template: path.join( __dirname, `src/${pageName}/index.html` ), filename: `${pageName}.html`, chunks: ["commons", pageName], inject: true, minify: { html5: true, collapseWhitespace: true, preserveLineBreaks: false, minifyCSS: true, minifyJS: true, removeComments: false } }) ); // console.log("pageName", match, match[1], pageName, entry); }); optimization: { splitChunks: { minSize: 0, // minSize: 1000, //当有公共的文件小于1000时,就不打包,还是嵌入在文件里 cacheGroups: { commons: { name: "commons", chunks: "all", minChunks: 2 //commons要引入2次以上就打出包来,小于2次不打 } } } }
-
摇树优化 必须是
ES6
语法,CJS
方式不支持-
tree shaking
-
DCE(Elimination)
-
tree shaking 原理:没有用到的代码,增加注释做标记,打包的时候擦除
利用
ES6
模块的特点:- 只能作为模块顶层的语句出现
- import 的模块名只能是字符串常量
- import bingding 是 immutable 的
代码擦除:uglify 阶段删除无用代码
// tree-shaking.js export function a() { return "This is func a"; } export function b() { return "This is func b"; }
-
-
构建后的代码存在大量闭包代码
-
代码分割和动态 import
- 懒加载 JS 脚本的方式
- CommonJS:require.ensure
- ES6:动态 import(目前还没有原生支持,需要 babel 转换)
- 如何使用动态 import?
npm i install @babel/plugin-syntax-dynamic-import -D
import("./text.js").then(Text => { console.log(Text); this.setState({ Text: Text.default }); }); //babelrc { "presets": ["@babel/preset-env", "@babel/preset-react"], "plugins": ["@babel/plugin-syntax-dynamic-import"] }
- 懒加载 JS 脚本的方式
-
webpack 和 ESLint 结合
-
alloyteam 团队 eslint-config-alloy(https://github.com/AlloyTeam/eslint-config-alloy)
-
ivweb 团队:eslint-config-ivweb(https://github.com/feflow/eslint-config-ivweb)
-
ESlint 如何执行落地?
-
和 CI/CD 系统集成
方案一 webpack 与 CI/CD 集成
本地开发阶段增加 precommit 钩子
-
和 webpack 集成
方案二 webpack 与 ESLint 集成(适合新项目,一开始重新做的项目)
npm i eslint eslint-plugin-import eslint-plugin-react eslint-plugin-jsx-a11y -D npm i eslint-loader -D npm i babel-eslint -D npm i eslint-config-airbnb -D
ESLint 官网文档 ESLint rules
//.eslintrc.js module.exports = { parser: "babel-eslint", extends: "airbnb", // extends: "eslint:recommended", env: { browser: true, node: true, commonjs: true, es6: true }, rules: { indent: ["error", 4], quotes: [2, "double"], semi: ["error", "always"], "comma-dangle": [ "error", { arrays: "never", objects: "never", imports: "never", exports: "never", functions: "never" } ], "no-console": 0, "no-unused-vars": "off", "react/jsx-indent": "off", "react/jsx-filename-extension": "off", "react/jsx-indent-props": "off", "react/react-in-jsx-scope": "off", "react/self-closing-comp": "off", "react/jsx-closing-tag-location": "off", "jsx-a11y/no-noninteractive-element-interactions": "off", "jsx-a11y/click-events-have-key-events": "off", "react/jsx-one-expression-per-line": "off", "react/jsx-one-expression-per-line": "off", "react/jsx-fragments": "off", "lines-between-class-members": "off", "arrow-parens": "off", "import/no-extraneous-dependencies": "off", "prefer-rest-params": "off", strict: "off", "import/newline-after-import": "off", "import/prefer-default-export": "off" } }; //webpack module: { rules: [ { test: /.js$/, use: ["babel-loader", "eslint-loader"] } ]; }
-
-
-
webpack 打包库和组件 引用我们写好的组件库 源码分支在 large-number 上
sudo npm i large-number-wfc@1.0.4 -D
-
SSR 代码实现思路
-
服务端
- 使用
react-dom/server
的renderToString
方法将 React 组件渲染成字符串 - 服务端路由返回对应的模版
- 使用
-
客户端
打包出针对服务端的组件
-