阅读 199

webpack入门与进阶:进阶用法(三)

自动清理构建目录

避免构建前每次都需要手动删除dist,可以通过clean-webpack-plugin自动清理。 webpack4.0配置

npm install clean-webpack-plugin -D 复制代码

webpack.prod.js

// webpack.prod.js // 自动清理构建目录  const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = {     ...     ...     plugins: [         new CleanWebpackPlugin(),     ] } 复制代码

webpack5.0配置, 无需下载插件官方文档

output: {     path: path.resolve(__dirname, 'dist'),     // path: path.join(__dirname, 'dist'),     // 使用 [name]占位符  设置文件指纹     filename: '[name]_[chunkhash:8].js',     // webpack5.0 清理构建目录配置     clean: true,  }, 复制代码

自动添加CSS3前缀

不同浏览器,内核不一样,css3需要补全不同的前缀,实现兼容问题。 image.png

使用PostCSS插件:autoprefixer自动补齐CSS3前缀

autoprefixer是css的后置处理器,而less或者sass是css预处理器。预处理器是打包前去处理,而autoprefixer是代码打包生成之后,才开始处理。
可以在caniuse查看样式兼容问题。
通常autoprefixer会跟postcss配合使用。

npm install --save-dev postcss-loader postcss配合使用。 autoprefixer 复制代码

postcss-loader 执行顺序必须保证在 css-loader 之前,建议还是放在 less或者 sass 等预处理器之后更好。即 loader 顺序:
less-loader -> postcss-loader -> css-loader -> style-loader 或者 MiniCssExtractPlugin.loader

webpack.prod.js

{ test: /\.less$/, use: [   // 'style-loader',   MiniCssExtractPlugin.loader,   'css-loader',   'less-loader',   { loader: "postcss-loader" } ] }, 复制代码

根目录新建postcss.config.js

module.exports = {   plugins: [     require('autoprefixer')()   ] } 复制代码

package.json

"browserslist": [     "defaults",     "not ie < 11",     "last 2 version",     "> 1%",     "ios 7",     "last 3 ios version"   ] 复制代码

解决webpack autoprefixer配置不生效

移动端CSS px 自动转换成rem

为适配不同的机型,实现响应式布局,可使用rem作为尺寸单位。
使用 px2rem-loader。
页面渲染是计算根元素的font-size。lib-flexible库
现在大部分使用viewport去兼容不同的浏览器。
viewport  待更新...

资源内联

资源内联的意义
代码层面:

  • 页面框架的初始化脚本

  • 上报相关打点

  • css内联避免页面闪动

请求层面:减少HTTP网络请求数

  • 小图片或者字体内联(url-loader)

HTML和JS内联

raw-loader内联html,js

CSS内联

  • 方案一:借助style-loader 把 CSS 插入到 DOM 中。

  • 方案二:html-inline-css-webpack-plugin

多页面应用

每⼀次⻚⾯跳转的时候,后台服务器都会给返回⼀个新的 html ⽂档,这种类型的⽹站也就是多⻚⽹站,也叫做多⻚应⽤。
动态获取entry和设置html-webpack-plugin数量
利用glob.sync,以同步的方式查询文件。
glob.sync(path.join(__dirname, './src/*/index.js'));查询根目录下的文件夹下的所有index.js文件

npm i glob -D 复制代码

webpack.prod.js

const glob = require('glob'); const setMPA = () => {   const entry = {};   const htmlWebpackPlugins = [];   // 查找出这个项目下的所有index.js文件   const entryFiles = glob.sync(path.join(__dirname, './src/*/index.js'));   // console.log(entryFiles, 'entryFiles')   // [ '/Users/**/program/webpack-demo/src/index/index.js','/Users/**/program/webpack-demo/src/search/index.js' ]   Object.keys(entryFiles).map(index => {     const entryFile = entryFiles[index];     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`,         // 指定生成的html需要哪些chunk         chunks: [pageName],         inject: true,         minify: {           html5: true,           collapseWhitespace: true,           preserveLineBreaks: false,           minifyCSS: true,           minifyJS: true,           removeComments: false         }       })     );   })   return {     entry,     htmlWebpackPlugins   } } const { entry, htmlWebpackPlugins } = setMPA(); module.exports = {     entry,     ...     plugins: [         ...htmlWebpackPlugins,         // 压缩css         new MiniCssExtractPlugin({           filename: '[name]_[contenthash:8].css'         }),         new OptimizeCSSAssetsPlugin(),     ] } 复制代码

Source map

作⽤:通过 source map 定位到源代码。
简单说,Source map就是一个信息文件,里面储存着位置信息。也就是说,转换后的代码的每一个位置,所对应的转换前的位置。
基本上开发环境直接用source-map。
production环境就把source-map添加到Error Reporting Tool(e.g. Sentry)上。这样既不直接暴露源代码,也能方便解决production环境遇到的bug。
source map科普⽂:www.ruanyifeng.com/blog/2013/0… \

source-map关键字

  • eval:使用eval包裹模块代码

  • source-map:产生.map文件

  • cheap 不包含列信息

  • inline 将.map作为DataURI嵌入,不单独生成.map文件

  • module: 包含loader的sourcemap

souce-map类型 image.png

devtool配置对应打包后的文件差异

// webpack.prod.js module.exports = {     ...     mode: 'none',     devtool: 'eval' } 复制代码

1、设置为eval,打包后,会有eval包裹

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _helloWorld__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);\n\nsetTimeout(function () {\n  document.write((0,_helloWorld__WEBPACK_IMPORTED_MODULE_0__.helloWorld)());\n});\n\n//# sourceURL=webpack://webpack-demo/./src/index/index.js?"); 复制代码

2、设置为source-map
js会和map内容进行分离 image.png 3、设置为inline-source-map
js会和map内容不会进行分离

基础库分离

如果需要引用一个库,但是又不想让webpack打包(减少打包的时间),并且又不影响我们在程序中以CMD、AMD或者window/global全局等方式进行使用(一般都以import方式引用使用),那就可以通过配置externals。
这样做的目的就是将不怎么需要更新的第三方库脱离webpack打包,不被打入bundle中,从而减少打包时间,但又不影响运用第三方库的方式,例如import方式等。

方式一:使用html-webpack-externals-plugin

  • 思路:将react、react-dom基础包通过cdn引入,不打入bundle中

  • 方法:使用html-webpack-externals-plugin

npm install --save-dev html-webpack-externals-plugin // npm@6+ npm install html-webpack-externals-plugin -D --legacy-peer-deps 复制代码

1、配置webpack.prod.js

// 分离基础库 const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin'); module.export = {     ...     plugins: [         ...htmlWebpackPlugins,     // 压缩css     new MiniCssExtractPlugin({       filename: '[name]_[contenthash:8].css'     }),     new OptimizeCSSAssetsPlugin(),     new HtmlWebpackExternalsPlugin({       externals: [             {               module: 'react',               entry: 'https://11.url.cn/now/lib/16.2.0/react.min.js',               global: 'React',             },             {               module: 'react-dom',               entry: 'https://11.url.cn/now/lib/16.2.0/react-dom.min.js',               global: 'ReactDOM',             },           ]        }),     ] } 复制代码

2、在src/search/index.html页面引入react react-dom包

<!DOCTYPE html> <html> <head>   <meta charset="UTF-8">   <meta name="viewport" content="width=device-width, initial-scale=1.0">   <meta http-equiv="X-UA-Compatible" content="ie=edge">   <title>Document</title> </head> <body>   <div id="root"></div>   // 引入react react-dom包   <script type="text/javascript" src="https://unpkg.com/react@17/umd/react.production.min.js"></script>   <script type="text/javascript" src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script> </body> </html> 复制代码

分离react react-dom包之后,打包之后的体积大大缩小 image.png

关于安装可能出现的问题:
如果npm@6.0+,下载会报错,错误如下:

image.png 错误解决方案:
gitee.com/vincentyun/…
www.it1352.com/2315867.htm…
找了好久才解决,原来因为npm7.x对某些事情比npm6.x更严格。
通常,最简单的解决方法是将--legacy-peer-deps标志传递给 npm ( npm install --legacy-peer-deps ),或者使用npm@6。
如果这不能立即起作用,也许可以先删除node_modules和package-lock.json。它们将被重新创建。
提示:使用npm@6不需要卸载npm@7。使用npx指定npm的版本。例如:npx -p npm@6 npm i --legacy-peer-deps。

image.png

方式二:利用splitChunks进行公共脚本分离(去掉html-webpack-externals-plugin相关配置)

从 webpack v4 开始内置了SplitChunksPlugin,直接通过optimization.splitChunks配置 webpack.prod.js

const setMPA = () => {     ...     htmlWebpackPlugins.push(         new HtmlWebpackPlugin({             chunks: ['vendors', pageName],//此处多配置一个vendors,与cacheGroups中的name对应         })     ) } module.exports = {     ...     optimization: {     // splitChunks分离基础包     splitChunks: {       cacheGroups: {         commons: {           test: /(react|react-dom)/,           // vendors需要添加到htmlWebpackPlugins的chunk里           name: 'vendors',           chunks: 'all'         }       }     },   },   // source-map,产生.map文件   devtool: 'inline-source-map', } 复制代码

image.png 执行npm run build编译打包之后,在html文件里,会引入这个vendors

image.png image.png

tree shaking(摇树优化)

1、概念
1个模块可能有多个⽅法,只要其中的某个⽅法使⽤到了,则整个⽂件都会被打到bundle ⾥⾯去,tree shaking 就是只把⽤到的⽅法打⼊ bundle ,没⽤到的⽅法会在uglify 阶段被擦除掉。
2、使用
webpack 默认⽀持,在 .babelrc ⾥设置 modules: false 即可
production mode的情况下默认开启
3、要求
必须是ES6的语法,CJS的方式不支持
4、DCE (Dead code elimination)

  • 代码不会被执行,不可到达

  • 代码执行的结果不会被用到

  • 代码只会影响死变量(只写不读)

if(false){     console.log('这段代码永远不会被执行') } 复制代码

原理

利⽤ ES6 模块的特点:

  • 只能作为模块顶层的语句出现

  • import 的模块名只能是字符串常量

  • import binding 是 immutable的

代码擦除:uglify 阶段删除⽆⽤代码
(不懂......)

代码分割和动态import

一、代码分割

1、意义

对于⼤的 Web 应⽤来讲,将所有的代码都放在⼀个⽂件中显然是不够有效的,特别是当你的某些代码块是在某些特殊的时候才会被使⽤到。webpack 有⼀个功能就是将你的代码库分割成chunks(语块),当代码运⾏到需要它们的时候再进⾏加载。

2、适用场景

  • 抽离相同代码到一个共享块

  • 脚本懒加载,使得初始下载的代码更小

二、动态import

懒加载JS脚本的方式:

  • commonJS:require.ensure

  • es6:动态import(目前还没有原生支持,需要babel转换)

1、安装插件
npm install @babel/plugin-syntax-dynamic-import --save-dev
2、在.babelrc文件里加入这个插件

{   "presets": [     "@babel/preset-env",     "@babel/preset-react"   ],   "plugins": [     "@babel/plugin-syntax-dynamic-import"   ] } 复制代码

3、新建text.js

import React from 'react'; export default () => <div>动态 import</div> 复制代码

4、在src/search/index.js文件里引入

'use strict'; import React from 'react'; import ReactDom from 'react-dom'; import './search.less' import imgSrc from './images/2.png' class Search extends React.Component{   constructor() {     super()     this.state = {       Text: null     }   }   render() {     // debugger     const { Text } = this.state;     return <div className="search-text">       <div>Search Text111</div>       <img className="img" src={imgSrc} onClick={this.loadComponent.bind(this)}></img>       {/* 渲染页面 */}       {         Text ? <Text/> : null       }     </div>   }   loadComponent() {     // 动态引入文件     import('./text.js').then((Text) => {       this.setState({         Text: Text.default,       })     })   } } ReactDom.render(   <Search/>,   document.getElementById('root') ) 复制代码

5、执行npm run build,打包完成之后会多一个数字开头的js文件,就是动态引入的文件。开头的数字就是懒加载的id。

image.png

点击图片,就会加载js

image.png

在webpack中使用ESLint

⾏业⾥⾯优秀的 ESLint 规范实践
腾讯:

  • alloyteam团队 eslint-config-alloy

  • ivweb 团队:eslint-config-ivweb

一、webpack与CI/CD集成

(loading......)

二、webpack与ESLint集成

1、在react中集成 1.1 安装

npm install --save-dev eslint @babel/eslint-parser @babel/preset-react@latest eslint-plugin-react eslint-config-alloy 复制代码

npm install eslint-loader babel-eslint -D 复制代码

npm install eslint-config-airbnb -D 复制代码

1.2webpack.prod.js中加入eslint-loader

module.exports = {     module: {         rules: [             {                 test: /\.js$/,                 // 添加eslint-loader                 use: ['babel-loader', 'eslint-loader'],              },         ]     } } 复制代码

1.3 配置.eslintrc.js
eslint官网

module.exports = {   parser: 'babel-eslint',   // 继承   extends: ['airbnb'],   // 环境变量   env: {     browser: true,     node: true,   }, }; 复制代码

在webpack中打包组件和基础库

webpack 除了可以⽤来打包应⽤,也可以⽤来打包 js 库,实现⼀个⼤整数加法库的打包。

  • 需要打包压缩版和⾮压缩版本

  • 打包好的组件和基础库,⽀持 AMD/CommonJS/ESModule 模块引⼊,也支持直接通过script引入

如何将库暴露出去?

  • library:指定库的全局变量

  • libraryTarget:支持库引入的方式

创建一个简单的组件库

1、新建一个项目larger-num,初始化该项目

npm init -y 复制代码

npm install webpack webpack-cli 复制代码

在根目录下新建webpack.config.jssrc/index.js

image.png 2、src/index.js

导出一个方法

export default function add(a,b) {   let i = a.length - 1;   let j = b.length - 1;   // 进位   let carry = 0;   let ret = "";   while(i >= 0 || j >= 0) {     let x = 0,y = 0, sum;     if (i >= 0) {       x = a[i] - '0';       i--;     }     if (j >= 0) {       y = b[j] - '0';       j--;     }     sum = x + y + carry;     if (sum >= 10) {       carry = 1;       sum -= 10;     } else {       carry = 0;     }     // 0+''     ret = sum + ret;   }   if (carry) {     ret = carry + ret;   }   return ret; } // add('999','1') // add('1','999') // add('123','2123') 复制代码

3、配置打包信息webpack.config.js webpack.docschina.org/configurati…

module.exports = {   entry: {     'large-number': './src/index.js',     'large-number.min': './src/index.js',   },   // 导出一个库   output: {     filename: '[name].js',     library: {       // 配置库的名字       name: "largeNumber",       // 配置将库暴露的方式       type: "umd",       export: "default"     },     // library: 'largeNumber',     // libraryTarget: 'umd',   },   mode: "production", } 复制代码

4、打包

添加一个script

"scripts": {     "test": "echo \"Error: no test specified\" && exit 1",     "build": "webpack"   }, 复制代码

执行打包命令

npm run build 复制代码

打包结果 image.png

image.png

只打.min文件压缩

webpack.docschina.org/plugins/ter…

1、下载terser-webpack-plugin

npm install terser-webpack-plugin --save-dev 复制代码

2、配置webpack.config.js

const TerserPlugin = require("terser-webpack-plugin"); module.exports = {   ...   ...   mode: "none",   optimization: {     minimize: true,     minimizer: [       // 通过 include 设置只压缩 min.js 结尾的⽂件       new TerserPlugin({         include: /\.min\.js$/,       })     ]   }, } 复制代码

3、执行npm run build,可以看到文件大小有明显变化,dist目录下的生成了一个压缩的一个未压缩的文件

image.png

image.png

4、发布到npm

如何发布npm包

webpack实现SSR打包

页面打开过程(客户端渲染)

客户端渲染,页面整体的打包过程如下:

用户点击一个按钮,会加载一个新的webview,加载完新的webview之后,开始加载新的页面

  • 页面开始加载,此时会出现一段时间的白屏时间,页面没有内容(因为还没加载html)

  • HTML加载成功,开始加载数据

    • 此时会有loading图标等等,告诉用户页面正在加载

    • 浏览器开始请求CSS、JS

    • 解析和执行CSS,页面上就会出现一些样式

    • 解析和执行JS,会执行JS的一些逻辑,比如请求图片资源,请求数据等等

  • 数据加载成功,渲染成功开始,加载图片资源

  • 图片加载成功,页面可交互

缺点:整个过程是串行过程,白屏时间长

服务端渲染(SSR)是什么

渲染: HTML + CSS + JS + Data -> 渲染后的 HTML

服务端渲染:

  • 所有模板等资源都存储在服务端,可将多个请求数量,优化成一个,是一个并行的加载

  • 客户端渲染是依赖用户的网络,而服务端渲染是利用内⽹机器拉取数据,加载速度更快

  • ⼀个 HTML 返回所有数据

优势:

  • 减少白屏时间

  • 对于SEO友好

浏览器和服务器交互过程

image.png

具体过程:

  • 页面请求开始,请求会到达服务端(server)

  • 服务端(server)会拿到HTML模板(HTML templete)和页面数据(data),渲染引擎会将HTML templete和data会进行server render

  • server render之后,浏览器会解析并渲染拼装好的HTML文件,此时,用户可以看到页面

  • 再加载并执行JS文件和其他资源文件,页面到达完全可交互的状态

客户端渲染 VS 服务端渲染

image.png

总结:

  • 客服端渲染是一个前端的JS渲染,而服务端渲染是在node端等底层语言里进行的渲染

  • 服务端渲染 (SSR) 的核⼼是减少请求


作者:丝绒拿铁有点甜
链接:https://juejin.cn/post/7031448248573231134

文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐