继 24 个实例入门并掌握「Webpack4」(一) 后续:css
demo9 源码地址html
什么是 Tree Shaking?vue
字面意思是摇树,项目中没有使用的代码会在打包的时候丢掉。JS 的 Tree Shaking 依赖的是 ES6 的模块系统(好比:import 和 export)node
项目目录以下:jquery
在 util.js 文件中写入测试代码webpack
// util.js export function a() { return 'this is function "a"' } export function b() { return 'this is function "b"' } export function c() { return 'this is function "c"' }
在 app.js 中引用 util.js 的 function a() 函数,按需引入:ios
// app.js import { a } from './vendor/util' console.log(a())
命令行运行 webpack 打包后,打开打包后生成的 /dist/app.bundle.js 文件。查找咱们 a()
函数输出的字符串,以下图所示:nginx
若是将查找内容换成 this is function "c"
或者 this is function "b"
, 并无相关查找结果。说明 JS Tree Shaking 成功。git
1. 如何处理第三方 JS 库?github
对于常用的第三方库(例如 jQuery、lodash 等等),如何实现 Tree Shaking ?
下面以 lodash.js 为例,进行介绍。
安装 lodash.js : npm install lodash --save
在 app.js 中引用 lodash.js 的一个函数:
// app.js import { chunk } from 'lodash' console.log(chunk([1, 2, 3], 2))
命令行打包。以下图所示,打包后大小是 70kb。显然,只引用了一个函数,不该该这么大。并无进行 Tree Shaking。
开头讲过,js tree shaking 利用的是 ES 的模块系统。而 lodash.js 使用的是 CommonJS 而不是ES6 的写法。因此,安装对应的模块系统便可。
安装 lodash.js 的 ES 写法的版本:npm install lodash-es --save
修改一下 app.js:
// app.js import { chunk } from 'lodash-es' console.log(chunk([1, 2, 3], 2))
再次打包,打包结果只有 3.5KB(以下图所示)。显然,tree shaking 成功。
在一些对加载速度敏感的项目中使用第三方库,请注意库的写法是否符合 ES 模板系统规范,以方便 webpack 进行 tree shaking。
CSS Tree Shaking 并不像 JS Tree Shaking 那样方便理解,首先要模拟一个真实的项目环境,来体现 CSS 的 Tree Shaking 的配置和效果。
此章节源码基于第八节处理 CSS 项目上作修改
咱们首先编写 /src/css/base.css 样式文件,在文件中,咱们编写了 3 个样式类。但在代码中,咱们只会使用 .box 和 .box--big 这两个类。代码以下所示:
/* base.css */ html { background: red; } .box { height: 200px; width: 200px; border-radius: 3px; background: green; } .box--big { height: 300px; width: 300px; border-radius: 5px; background: red; } .box-small { height: 100px; width: 100px; border-radius: 2px; background: yellow; }
按照正常使用习惯,DOM 操做来实现样式的添加和卸载,是一向技术手段。因此,入口文件 /src/app.js
中建立了一个 <div>
标签,而且将它的类设为 .box
// app.js import base from './css/base.css' // 给 app 标签再加一个 div 而且类名为 box var app = document.getElementById('app') var div = document.createElement('div') div.className = 'box' app.appendChild(div)
最后,为了让环境更接近实际环境,咱们在 index.html
的一个标签,也引用了定义好的 box-big 样式类。
<!DOCTYPE html> <html lang="en"> <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>CSS Tree Shaking</title> </head> <body> <div id="app"> <div class="box-big"></div> </div> </body> </html>
PurifyCSS将帮助咱们进行 CSS Tree Shaking 操做。为了能准确指明要进行 Tree Shaking 的 CSS 文件,还有 glob-all (另外一个第三方库)。
glob-all 的做用就是帮助 PurifyCSS 进行路径处理,定位要作 Tree Shaking 的路径文件。
安装依赖:
npm i glob-all purify-css purifycss-webpack --save-dev
更改配置文件:
const path = require('path') const CleanWebpackPlugin = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 将 css 单独打包成文件 const PurifyCSS = require('purifycss-webpack') const glob = require('glob-all') module.exports = { entry: { app: './src/app.js' }, output: { publicPath: './', // js 引用的路径或者 CDN 地址 path: path.resolve(__dirname, 'dist'), // 打包文件的输出目录 filename: '[name].bundle.js', // 代码打包后的文件名 chunkFilename: '[name].js' // 代码拆分后的文件名 }, module: { rules: [ { test: /\.css$/, // 针对 .scss 或者 .css 后缀的文件设置 loader use: [ { loader: MiniCssExtractPlugin.loader }, 'css-loader' ] } ] }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ // 打包输出HTML title: '自动生成 HTML', minify: { // 压缩 HTML 文件 removeComments: true, // 移除 HTML 中的注释 collapseWhitespace: true, // 删除空白符与换行符 minifyCSS: true // 压缩内联 css }, filename: 'index.html', // 生成后的文件名 template: 'index.html', // 根据此模版生成 HTML 文件 chunks: ['app'] // entry中的 app 入口才会被打包 }), new MiniCssExtractPlugin({ filename: '[name].css', chunkFilename: '[id].css' }), // 清除无用 css new PurifyCSS({ paths: glob.sync([ // 要作 CSS Tree Shaking 的路径文件 path.resolve(__dirname, './*.html'), // 请注意,咱们一样须要对 html 文件进行 tree shaking path.resolve(__dirname, './src/*.js') ]) }) ] }
打包完查看 dist/app.css 文件
在 index.html 和 src/app.js 中引用的样式都被打包了,而没有被使用的样式类–box-small,没有被打包进去
目录结构:
webpack4 中的图片经常使用的基础操做:
如项目代码目录展现的那样,除了常见的 app.js
做为入口文件,咱们将用到的 3 张图片放在 /src/assets/imgs/
目录下,并在样式文件 base.css
中引用这些图片。
剩下的内容交给 webpack
打包处理便可。样式文件和入口 app.js
文件的代码分别以下所示:
/* base.css */ *, body { margin: 0; padding: 0; } .box { height: 400px; width: 400px; border: 5px solid #000; color: #000; } .box div { width: 100px; height: 100px; float: left; } .box .ani1 { background: url('./../assets/imgs/1.jpg') no-repeat; } .box .ani2 { background: url('./../assets/imgs/2.png') no-repeat; } .box .ani3 { background: url('./../assets/imgs/3.png') no-repeat; }
在 app.js
中
import './css/base.css'
安装依赖:
npm install url-loader file-loader --save-dev
在 webpack.config.js
中的 module.rules 选项中进行配置,以实现让 loader 识别图片后缀名,而且进行指定的处理操做。
module.exports = { module: { rules: [ { test: /\.(png|jpg|jpeg|gif)$/, use: [ { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', outputPath: 'images/', //输出到 images 文件夹 limit: 20000 //把小于 20kb 的文件转成 Base64 的格式 } } ] } ] } }
完整的配置文件
const path = require('path') const CleanWebpackPlugin = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 将 css 单独打包成文件 module.exports = { entry: { app: './src/app.js' }, output: { publicPath: './', // js 引用的路径或者 CDN 地址 path: path.resolve(__dirname, 'dist'), // 打包文件的输出目录 filename: '[name].bundle.js', // 代码打包后的文件名 chunkFilename: '[name].js' // 代码拆分后的文件名 }, module: { rules: [ { test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader }, 'css-loader' ] }, { test: /\.(png|jpg|jpeg|gif)$/, use: [ { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', outputPath: 'images/', //输出到 images 文件夹 limit: 20000 //把小于 20kb 的文件转成 Base64 的格式 } } ] } ] }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ // 打包输出HTML title: '自动生成 HTML', minify: { // 压缩 HTML 文件 removeComments: true, // 移除 HTML 中的注释 collapseWhitespace: true, // 删除空白符与换行符 minifyCSS: true // 压缩内联 css }, filename: 'index.html', // 生成后的文件名 template: 'index.html', // 根据此模版生成 HTML 文件 chunks: ['app'] // entry中的 app 入口才会被打包 }), new MiniCssExtractPlugin({ filename: '[name].css', chunkFilename: '[id].css' }) ] }
打包项目,查看打包结果,并在浏览器中打开 index.html
文件
能够看到除了 1.jpg,另外两张图片已经被打包成 base64
格式,在 app.css
文件中
1.jpg 这个文件超过咱们在 url-loader 选项中设置的 limit 值,因此被单独打包
这就是利用了 file-loader 的能力,若是在 url-loader 中设置了 limit 的值,却没有安装 file-loader 依赖,会怎么样?来试试看,首先卸载 file-loader 依赖,npm uninstall file-loader
,再运行打包命令,npm run build
若是图片较多,会发不少 http 请求,会下降页面性能。url-loader 会将引入的图片编码,转为
base64
字符串。再把这串字符打包到文件中,最终只须要引入这个文件就能访问图片了,节省了图片请求。
可是,若是图片较大,编码会消耗性能。所以 url-loader 提供了一个 limit 参数,小于 limit 字节的文件会被转为 base64
,大于 limit 的使用 file-loader 进行处理,单独打包。
url-loader 依赖 file-loader,url-loader 能够看做是加强版的 file-loader
图片压缩须要使用 img-loader 插件,除此以外,针对不一样的图片类型,还要引用不一样的插件。好比,咱们项目中使用的是 png 图片,所以,须要引入 imagemin-pngquant
,而且指定压缩率。压缩 jpg/jpeg 图片为 imagemin-mozjpeg
插件
这里有个 bug,能够先不急着操做,先把这一小节看完,再决定!!
安装依赖
npm i img-loader imagemin imagemin-pngquant imagemin-mozjpeg --save-dev
在以前的配置上更改:
{ test: /\.(png|jpg|jpeg|gif)$/, use: [ { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', outputPath: 'images/', // 输出到 images 文件夹 limit: 20000 //把小于 20kb 的文件转成 Base64 的格式 } } ] }
更改成:
{ test: /\.(png|jpg|jpeg|gif)$/, use: [ { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', limit: 1000, // size <= 1KB outputPath: 'images/' } }, // img-loader for zip img { loader: 'img-loader', options: { plugins: [ require('imagemin-pngquant')({ quality: '80' // the quality of zip }), require('imagemin-mozjpeg')({ quality: '80' }) ] } } ] }
打包结果:
缘由在 png 图片上,jpg 图片能够压缩,可是去 imagemin-pngquant github 上也没发现有人提出相似 issue ,百度、google 找了半天,仍是没发现怎么解决 😭,因而使用另外一种压缩图片的插件 image-webpack-loader
首先卸载了以前的依赖:
npm uni img-loader imagemin imagemin-pngquant imagemin-mozjpeg
安装依赖:
npm i image-webpack-loader --save-dev
这个依赖安装的时间会比较久。。。能够先去作别的。。。
在以前的配置上更改:
{ test: /\.(png|jpg|jpeg|gif)$/, use: [ { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', outputPath: 'images/', // 输出到 images 文件夹 limit: 20000 //把小于 20kb 的文件转成 Base64 的格式 } } ] }
更改成:
{ test: /\.(png|jpg|jpeg|gif)$/, use: [ { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', limit: 1000, // size <= 1KB outputPath: 'images/' } }, // img-loader for zip img { loader: 'image-webpack-loader', options: { // 压缩 jpg/jpeg 图片 mozjpeg: { progressive: true, quality: 65 // 压缩率 }, // 压缩 png 图片 pngquant: { quality: '65-90', speed: 4 } } } ] }
这里故意把 url-loader 的 limit 属性值设的很小,不让它转化 png 图片为 base64
,由于咱们要测试压缩 png 图片
打包结果:
图片压缩成功,这里我仔细看了下image-webpack-loader 的 github,其实这个 image-webpack-loader
插件内置了好几种图片压缩的插件
这里让我很疑惑,为何我直接安装 imagemin-pngquant
不行,反而使用 image-webpack-loader
却能够,因而我去查看 package-lock.json
文件,搜索 image-webpack-loader
我看了下我以前安装的是最新的版本, ^7.0.0 !!!
阿西吧... 终于找到问题所在,新版本有些问题没处理好,致使压缩 png 图片失败,知道问题就好办了,在 package.json 中,将 imagemin-pngquant
版本改成 ^6.0.0,从新 npm install
再按照以前的操做,就能够压缩成了,对应版本以下:
{ "devDependencies": { "imagemin": "^6.1.0", "imagemin-mozjpeg": "^8.0.0", "imagemin-pngquant": "^6.0.0", "img-loader": "^3.0.1" } }
若是使用 image-webpack-loader
,版本为 4.6.0
,引入的依赖版本也在白框内
此次我仍是使用 image-webpack-loader
,朋友们能够自行选择使用哪一个插件,只是 image-webpack-loader
引入了其余图片格式压缩的依赖,如 svg/webp/gif 等,只安装 image-webpack-loader
就够了,而另外一种则是要一个个插件装过去,其实原理都同样
通过此次调试,明白 并非最新的版本就是最好的,新版本也许有哪些地方没处理好,或者是不能兼容其余插件,致使报错因此安装第三方依赖的时候,仍是要谨慎一点,npm install 默认是安装最新版,若是出了问题,回滚到以前的稳定版,不只仅适用于
webpack
插件,对于其余软件或者工具也是这样
写这一小节的时间为:2019-3-9
,以后的版本变更出现报错的话,能够不用安装最新版,回滚到以前的版本试试
安装依赖:
npm i postcss-loader postcss-sprites --save-dev
完整配置:
const path = require('path') const CleanWebpackPlugin = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 将 css 单独打包成文件 /*********** sprites config ***************/ let spritesConfig = { spritePath: './dist/images' } /******************************************/ module.exports = { entry: { app: './src/app.js' }, output: { publicPath: './', // js 引用的路径或者 CDN 地址 path: path.resolve(__dirname, 'dist'), // 打包文件的输出目录 filename: '[name].bundle.js', // 代码打包后的文件名 chunkFilename: '[name].js' // 代码拆分后的文件名 }, module: { rules: [ { test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader }, 'css-loader', /*********** loader for sprites ***************/ { loader: 'postcss-loader', options: { ident: 'postcss', plugins: [require('postcss-sprites')(spritesConfig)] } } /*********************************************/ ] }, { test: /\.(png|jpg|jpeg|gif)$/, use: [ { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', limit: 1000, // size <= 1KB outputPath: 'images/' } }, // img-loader for zip img { loader: 'image-webpack-loader', options: { // 压缩 jpg/jpeg 图片 mozjpeg: { progressive: true, quality: 65 // 压缩率 }, // 压缩 png 图片 pngquant: { quality: '65-90', speed: 4 } } } ] } ] }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ // 打包输出HTML title: '自动生成 HTML', minify: { // 压缩 HTML 文件 removeComments: true, // 移除 HTML 中的注释 collapseWhitespace: true, // 删除空白符与换行符 minifyCSS: true // 压缩内联 css }, filename: 'index.html', // 生成后的文件名 template: 'index.html', // 根据此模版生成 HTML 文件 chunks: ['app'] // entry中的 app 入口才会被打包 }), new MiniCssExtractPlugin({ filename: '[name].css', chunkFilename: '[id].css' }) ] }
打包后查看结果:
雪碧图是为了减小网络请求,因此被处理雪碧图的图片多为各式各样的 logo 或者大小相等的小图片。
而对于大图片,不推荐使用雪碧图。这样会使得图片体积很大
除此以外,雪碧图要配合 css 代码进行定制化使用。要经过 css 代码在雪碧图上精准定位须要的图片
项目目录为:
package.json 中使用的依赖以下:
{ "scripts": { "dev": "webpack --mode development", "build": "webpack --mode production" }, "devDependencies": { "clean-webpack-plugin": "^2.0.0", "css-loader": "^2.1.0", "file-loader": "^3.0.1", "url-loader": "^1.1.2", "html-loader": "^0.5.5", "html-webpack-plugin": "^3.2.0", "mini-css-extract-plugin": "^0.5.0", "webpack": "^4.29.6", "webpack-cli": "^3.2.3" } }
app.js 中引入字体文件
import './assets/fonts/iconfont.css'
配置 webpack.config.js 文件来处理字体
借助 url-loader,能够识别而且处理 eot、woff 等结尾的字体文件。同时,根据字体文件大小,能够灵活配置是否进行 base64 编码。
下面的 demo 就是当文件大小小于 5000B 的时候,进行 base64 编码。
const path = require('path') const CleanWebpackPlugin = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 将 css 单独打包成文件 module.exports = { entry: { app: './src/app.js' }, output: { publicPath: './', // js 引用的路径或者 CDN 地址 path: path.resolve(__dirname, 'dist'), // 打包文件的输出目录 filename: '[name].bundle.js', // 代码打包后的文件名 chunkFilename: '[name].js' // 代码拆分后的文件名 }, module: { rules: [ { test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader }, 'css-loader' ] }, { test: /\.(eot|woff2?|ttf|svg)$/, use: [ { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', limit: 5000, // fonts file size <= 5KB, use 'base64'; else, output svg file publicPath: 'fonts/', outputPath: 'fonts/' } } ] } ] }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ // 打包输出HTML title: '自动生成 HTML', minify: { // 压缩 HTML 文件 removeComments: true, // 移除 HTML 中的注释 collapseWhitespace: true, // 删除空白符与换行符 minifyCSS: true // 压缩内联 css }, filename: 'index.html', // 生成后的文件名 template: 'index.html', // 根据此模版生成 HTML 文件 chunks: ['app'] // entry中的 app 入口才会被打包 }), new MiniCssExtractPlugin({ filename: '[name].css', chunkFilename: '[id].css' }) ] }
在 index.html 中使用字体
<!DOCTYPE html> <html lang="en"> <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>处理字体文件</title> </head> <body> <div id="app"> <div class="box"> <i class="iconfont icon-xiazai"></i> <i class="iconfont icon-shoucang"></i> <i class="iconfont icon-erweima"></i> <i class="iconfont icon-xiangshang"></i> <i class="iconfont icon-qiehuanzuhu"></i> <i class="iconfont icon-sort"></i> <i class="iconfont icon-yonghu"></i> </div> </div> </body> </html>
打包后查看 index.html 文件,打包成功
项目目录:
1. 如何使用和管理第三方 JS 库?
项目作大以后,开发者会更多专一在业务逻辑上,其余方面则尽力使用第三方 JS 库来实现。
因为 js 变化实在太快,因此出现了多种引入和管理第三方库的方法,经常使用的有 3 中:
<script></script>
标签引入便可针对第三种方法,若是没有 webpack,则须要手动引入 import 或者 require 来加载文件;可是,webpack 提供了 alias 的配置,配合 webpack.ProvidePlugin 这款插件,能够跳过手动入,直接使用!
2. 编写入口文件
如项目目录图片所展现的,咱们下载了 jquery.min.js,放到了项目中。同时,咱们也经过 npm 安装了 jquery。
为了尽量模仿生产环境,app.js 中使用了 $ 来调用 jq,还使用了 jQuery 来调用 jq。
由于正式项目中,因为须要的依赖过多,挂载到 window 对象的库,很容易发生命名冲突问题。此时,就须要重命名库。例如:$ 就被换成了 jQuery。
在 app.js 中进行修改
// app.js $('div').addClass('new') jQuery('div').addClass('old') // 运行webpack后 // 浏览器打开 index.html, 查看 div 标签的 class
webpack.ProvidePlugin 参数是键值对形式,键就是咱们项目中使用的变量名,值就是键所指向的库。
webpack.ProvidePlugin 会先从 npm 安装的包中查找是否有符合的库
若是 webpack 配置了 resolve.alias 选项(理解成 “别名”),那么 webpack.ProvidePlugin 就会顺着这条链一直找下去。
const path = require('path') const webpack = require('webpack') const CleanWebpackPlugin = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { entry: { app: './src/app.js' }, output: { publicPath: './', // js 引用的路径或者 CDN 地址 path: path.resolve(__dirname, 'dist'), // 打包文件的输出目录 filename: '[name].bundle.js', // 代码打包后的文件名 chunkFilename: '[name].js' // 代码拆分后的文件名 }, resolve: { alias: { jQuery$: path.resolve(__dirname, 'src/vendor/jquery.min.js') } }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ // 打包输出HTML title: '自动生成 HTML', minify: { // 压缩 HTML 文件 removeComments: true, // 移除 HTML 中的注释 collapseWhitespace: true, // 删除空白符与换行符 minifyCSS: true // 压缩内联 css }, filename: 'index.html', // 生成后的文件名 template: 'index.html', // 根据此模版生成 HTML 文件 chunks: ['app'] // entry中的 app 入口才会被打包 }), new webpack.ProvidePlugin({ $: 'jquery', // npm jQuery: 'jQuery' // 本地Js文件 }) ] }
修改 index.html 文件
<!DOCTYPE html> <html lang="en"> <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>处理第三方 js 库</title> </head> <body> <div></div> </body> </html>
打包并在 Chrome 中打开 index.html。以下图所示,<div>
标签已经被添加上了 old 和 new 两个样式类。证实在 app.js 中使用的 $ 和 jQuery 都成功指向了 jquery 库。
1. 为何须要开发模式?
这十几节来咱们使用最多的就是生产环境,也就是执行 npm run build
命令,打包项目中的各类文件及压缩
而开发模式就是指定 mode 为 development。对应咱们在 package.json
中配置的,就是 npm run dev
,在第二小节也涉及到了这一点
在开发模式下,咱们须要对代码进行调试。对应的配置就是:devtool 设置为 source-map。在非开发模式下,须要关闭此选项,以减少打包体积。详情见: devtool
在开发模式下,还须要热重载、路由重定向、设置代理等功能,webpack4 已经提供了 devServer 选项,启动一个本地服务器,让开发者使用这些功能。
目录结构:
安装依赖
npm i webpack-dev-server --save-dev
修改 package.json
{ "scripts": { "dev": "webpack-dev-server --open", "build": "webpack --mode production" }, "devDependencies": { "clean-webpack-plugin": "^2.0.0", "html-loader": "^0.5.5", "html-webpack-plugin": "^3.2.0", "jquery": "^3.3.1", "webpack": "^4.29.6", "webpack-cli": "^3.2.3", "webpack-dev-server": "^3.2.1" } }
由于咱们在 package.json 中配置了 script,因此开启开发模式直接 npm run dev
便可
虽然控制台输出了打包信息(假设咱们已经配置了热重载),可是磁盘上并无建立 /dist/ 文件夹和打包文件。控制台的打包文件的相关内容是存储在内存之中的。
修改 index.html 文件
<!DOCTYPE html> <html lang="en"> <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>webpack-dev-server</title> </head> <body> This is Index html </body> </html>
按照项目目录,简单封装下 /vendor/ 下的三个 js 文件,以方便 app.js 调用:
// minus.js module.exports = function(a, b) { return a - b } // multi.js define(function(require, factory) { 'use strict' return function(a, b) { return a * b } }) // sum.js export default function(a, b) { console.log('I am sum.js') return a + b }
app.js 中使用三种引入方式引入 js 文件:
import sum from './vendor/sum' console.log('sum(1, 2) = ', sum(1, 2)) var minus = require('./vendor/minus') console.log('minus(1, 2) = ', minus(1, 2)) require(['./vendor/multi'], function(multi) { console.log('multi(1, 2) = ', multi(1, 2)) })
如今开始更改 webpack.config.js, 完整的配置文件以下:
const webpack = require('webpack') const path = require('path') const CleanWebpackPlugin = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { entry: { app: './src/app.js' }, output: { publicPath: '/', // js 引用的路径或者 CDN 地址 path: path.resolve(__dirname, 'dist'), // 打包文件的输出目录 filename: '[name].bundle.js', // 代码打包后的文件名 chunkFilename: '[name].js' // 代码拆分后的文件名 }, mode: 'development', // 开发模式 devtool: 'source-map', // 开启调试 devServer: { contentBase: path.join(__dirname, 'dist'), port: 8000, // 本地服务器端口号 hot: true, // 热重载 overlay: true, // 若是代码出错,会在浏览器页面弹出“浮动层”。相似于 vue-cli 等脚手架 proxy: { // 跨域代理转发 '/comments': { target: 'https://m.weibo.cn', changeOrigin: true, logLevel: 'debug', headers: { Cookie: '' } } }, historyApiFallback: { // HTML5 history模式 rewrites: [{ from: /.*/, to: '/index.html' }] } }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ // 打包输出HTML title: '自动生成 HTML', minify: { // 压缩 HTML 文件 removeComments: true, // 移除 HTML 中的注释 collapseWhitespace: true, // 删除空白符与换行符 minifyCSS: true // 压缩内联 css }, filename: 'index.html', // 生成后的文件名 template: 'index.html', // 根据此模版生成 HTML 文件 chunks: ['app'] // entry中的 app 入口才会被打包 }), new webpack.HotModuleReplacementPlugin(), // 热部署模块 new webpack.NamedModulesPlugin(), new webpack.ProvidePlugin({ $: 'jquery', // npm jQuery: 'jQuery' // 本地Js文件 }) ] }
对上面的配置进行单独分析:
模块热更新须要 HotModuleReplacementPlugin 和 NamedModulesPlugin 这两个插件,而且顺序不能错,而且指定 devServer.hot 为 true,
const webpack = require('webpack') // 引入 webpack module.exports = { plugins: [ new webpack.HotModuleReplacementPlugin(), // 热部署模块 new webpack.NamedModulesPlugin() ] }
有了这两个插件,在项目的 js 代码中能够针对侦测到变动的文件而且作出相关处理,也就不用写完代码从新刷新页面,它会自动检测变动的代码而且在页面上更改
注意是 js 代码,若是你去改动 index.html 文件,保存后,页面并不会更改,反之你去修改了 js 文件,保存后,页面会更新
好比,咱们启动开发模式后,修改了 vendor/sum.js
这个文件,此时,须要在浏览器的控制台打印一些信息。那么,app.js 中就能够这么写:
if (module.hot) { // 检测是否有模块热更新 module.hot.accept('./vendor/sum.js', function() { // 针对被更新的模块, 进行进一步操做 console.log('/vendor/sum.js is changed') }) }
每当 sum.js 被修改后,均可以自动执行回调函数。
浏览器控制台输出信息以下:
可是咱们平常开发中使用 vue 脚手架根本没有写过这样的代码,也能热更新,是由于 vue-loader 中内置了这种方法,css-loader 中也有,因此咱们改完 js 和 css 代码就能直接看到更新
随着先后端分离开发的普及,跨域请求变得愈来愈常见。为了快速开发,能够利用 devServer.proxy 作一个代理转发,来绕过浏览器的跨域限制。
devServer 模块的底层是使用了 http-proxy-middleware,能配置的东西很是多
按照前面的配置文件,若是想调用微博的一个接口:https://m.weibo.cn/comments/h...。只须要在代码中对 /comments/hotflow 进行请求便可,在 app.js 中添加以下代码:
$.get( '/comments/hotflow', { id: '4263554020904293', mid: '4263554020904293', max_id_type: '0' }, function(data) { console.log(data) } )
上面代码是使用 jQuery 发送 get 请求,若是是在 vue 项目中,通常是使用 axios 来发送请求
修改完 app.js 后保存,打开以前的 localhost:8000 网页,能够看到 Network 发送的请求
当项目使用 HTML5 History API 时,任意的 404 响应均可能须要被替代为 index.html。
在 SPA(单页应用)中,任何响应直接被替代为 index.html。
在 vuejs 官方的脚手架 vue-cli 中,开发模式下配置以下:
historyApiFallback: { // HTML5 history模式 rewrites: [{ from: /.*/, to: '/index.html' }] }
最终 app.js 中的代码以下:
import sum from './vendor/sum' console.log('sum(1, 2) = ', sum(1, 2)) var minus = require('./vendor/minus') console.log('minus(1, 2) = ', minus(1, 2)) require(['./vendor/multi'], function(multi) { console.log('multi(1, 2) = ', multi(1, 2)) }) $.get( '/comments/hotflow', { id: '4263554020904293', mid: '4263554020904293', max_id_type: '0' }, function(data) { console.log(data) } ) if (module.hot) { // 检测是否有模块热更新 module.hot.accept('./vendor/sum.js', function() { // 针对被更新的模块, 进行进一步操做 console.log('/vendor/sum.js is changed') }) }
打开控制台,能够看到代码都正常运行没有出错。除此以外,因为开启了 source-map,因此能够定位代码位置(下图红框内):
参考文章: webpack4 系列教程 (十五):开发模式与 webpack-dev-server
首先,新建一个文件夹:demo15,执行 npm init -y
初始化 package.json
,生成后的文件以下:
{ "name": "example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }
咱们先将无用的代码清除掉,只留下关键代码:
{ "scripts": {} }
首先安装 webpack 所需依赖
npm i webpack webpack-cli webpack-dev-server --save-dev
安装 babel7,由于目前主要是用 ES6 来编写代码,因此须要转译
npm i @babel/core babel-loader @babel/preset-env @babel/plugin-transform-runtime --save-dev
npm i @babel/polyfill @babel/runtime
如今 package.json 中的依赖为:
{ "scripts": {}, "devDependencies": { "@babel/core": "^7.3.4", "@babel/plugin-transform-runtime": "^7.3.4", "@babel/preset-env": "^7.3.4", "babel-loader": "^8.0.5", "webpack": "^4.29.6", "webpack-cli": "^3.2.3", "webpack-dev-server": "^3.2.1" }, "dependencies": { "@babel/polyfill": "^7.2.5", "@babel/runtime": "^7.3.4" } }
新建 .babelrc 来配置 babel 插件,代码以下:
{ "presets": ["@babel/preset-env"], "plugins": ["@babel/plugin-transform-runtime"] }
新建 .browserslistrc 文件配置该项目所支持的浏览器版本
# 所支持的浏览器版本 > 1% # 全球使用状况统计选择的浏览器版本 last 2 version # 每一个浏览器的最后两个版本 not ie <= 8 # 排除小于 ie8 及如下的浏览器
在开始配置 webpack.config.js 文件以前,须要注意一下,由于如今咱们是有两种模式, production(生产) 和 development(开发) 模式。
安装自动生成 html 依赖
npm i html-webpack-plugin html-loader clean-webpack-plugin --save-dev
安装 css/字体图标处理依赖
npm i css-loader style-loader mini-css-extract-plugin optimize-css-assets-webpack-plugin --save-dev
安装 scss 处理依赖
npm i node-sass sass-loader --save-dev
为不一样内核的浏览器加上 CSS 前缀
npm install postcss-loader autoprefixer --save-dev
图片及字体处理:
npm i url-loader file-loader image-webpack-loader --save-dev
第三方 js 库
npm i jquery
如今 package.json 为:
{ "scripts": {}, "devDependencies": { "@babel/core": "^7.3.4", "@babel/plugin-transform-runtime": "^7.3.4", "@babel/preset-env": "^7.3.4", "autoprefixer": "^9.4.10", "babel-loader": "^8.0.5", "clean-webpack-plugin": "^2.0.0", "css-loader": "^2.1.1", "file-loader": "^3.0.1", "html-loader": "^0.5.5", "html-webpack-plugin": "^3.2.0", "image-webpack-loader": "^4.6.0", "mini-css-extract-plugin": "^0.5.0", "node-sass": "^4.11.0", "optimize-css-assets-webpack-plugin": "^5.0.1", "postcss-loader": "^3.0.0", "sass-loader": "^7.1.0", "style-loader": "^0.23.1", "url-loader": "^1.1.2", "webpack": "^4.29.6", "webpack-cli": "^3.2.3", "webpack-dev-server": "^3.2.1" }, "dependencies": { "@babel/polyfill": "^7.2.5", "@babel/runtime": "^7.3.4", "jquery": "^3.3.1" } }
以前咱们大多都是写生产模式,也就是常常说的打包,可是咱们平常开发项目,用的是开发模式。
只有在项目作完后,要部署到 nginx 上的时候才使用生产模式,将代码打包后放到 nginx 中
之因此要分两种模式是由于,开发模式下,须要加快编译的速度,能够热更新以及设置跨域地址,开启源码调试(devtool: 'source-map')
而生成模式下,则须要压缩 js/css 代码,拆分公共代码段,拆分第三方 js 库等操做
因此这里的配置咱们分红三个文件来写,一个是生产配置,一个是开发配置,最后一个是基础配置
即:webpack.base.conf.js(基础配置)、webpack.dev.conf.js(开发配置)、webpack.prod.conf.js(生产配置)
新建 build 文件夹,建立上述三个文件,项目结构为:
这里须要使用到一个插件,webpack-merge 用来合并配置,好比开发环境就合并开发配置 + 基础配置,生产就合并生产配置 + 基础配置
npm i webpack-merge --save-dev
先简单写个 webpack.base.conf.js 的示例代码
const merge = require('webpack-merge') const productionConfig = require('./webpack.prod.conf') // 引入生产环境配置文件 const developmentConfig = require('./webpack.dev.conf') // 引入开发环境配置文件 const baseConfig = {} // ... 省略 module.exports = env => { let config = env === 'production' ? productionConfig : developmentConfig return merge(baseConfig, config) // 合并 公共配置 和 环境配置 }
在代码中区分不一样环境:
module.exports = env => { let config = env === 'production' ? productionConfig : developmentConfig return merge(baseConfig, config) // 合并 公共配置 和 环境配置 }
这里的 env 在 package.json 中进行配置,修改 scripts,添加 "dev" 和 "build" 命令
注意,这里有个 --env 字段,与 webpack.base.conf.js 中的 env 是联动的,告诉它当前是什么环境,而后合并成什么环境
{ "scripts": { "dev": "webpack-dev-server --env development --open --config build/webpack.base.conf.js", "build": "webpack --env production --config build/webpack.base.conf.js" } }
const webpack = require('webpack') const merge = require('webpack-merge') const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 将 css 单独打包成文件 const CleanWebpackPlugin = require('clean-webpack-plugin') const path = require('path') const productionConfig = require('./webpack.prod.conf.js') // 引入生产环境配置文件 const developmentConfig = require('./webpack.dev.conf.js') // 引入开发环境配置文件 /** * 根据不一样的环境,生成不一样的配置 * @param {String} env "development" or "production" */ const generateConfig = env => { // 将须要的 Loader 和 Plugin 单独声明 let scriptLoader = [ { loader: 'babel-loader' } ] let cssLoader = [ 'style-loader', 'css-loader', 'sass-loader', // 使用 sass-loader 将 scss 转为 css 'postcss-loader' // 使用 postcss 为 css 加上浏览器前缀 ] let cssExtractLoader = [ { loader: MiniCssExtractPlugin.loader }, 'css-loader', 'sass-loader', // 使用 sass-loader 将 scss 转为 css 'postcss-loader' // 使用 postcss 为 css 加上浏览器前缀 ] let fontLoader = [ { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', limit: 5000, // fonts file size <= 5KB, use 'base64'; else, output svg file publicPath: 'fonts/', outputPath: 'fonts/' } } ] let imageLoader = [ { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', limit: 10000, // size <= 10KB outputPath: 'images/' } }, // 图片压缩 { loader: 'image-webpack-loader', options: { // 压缩 jpg/jpeg 图片 mozjpeg: { progressive: true, quality: 50 // 压缩率 }, // 压缩 png 图片 pngquant: { quality: '65-90', speed: 4 } } } ] let styleLoader = env === 'production' ? cssExtractLoader // 生产环境下压缩 css 代码 : cssLoader // 开发环境:页内样式嵌入 return { entry: { app: './src/app.js' }, output: { publicPath: env === 'development' ? '/' : './', path: path.resolve(__dirname, '..', 'dist'), filename: '[name]-[hash:5].bundle.js', chunkFilename: '[name]-[hash:5].chunk.js' }, module: { rules: [ { test: /\.js$/, exclude: /(node_modules)/, use: scriptLoader }, { test: /\.(sa|sc|c)ss$/, use: styleLoader }, { test: /\.(eot|woff2?|ttf|svg)$/, use: fontLoader }, { test: /\.(png|jpg|jpeg|gif)$/, use: imageLoader } ] }, plugins: [ // 开发环境和生产环境两者均须要的插件 new HtmlWebpackPlugin({ title: 'webpack4 实战', filename: 'index.html', template: path.resolve(__dirname, '..', 'index.html'), // chunks: ['app'], minify: { collapseWhitespace: true } }), new webpack.ProvidePlugin({ $: 'jquery' }), new CleanWebpackPlugin() ] } } module.exports = env => { let config = env === 'production' ? productionConfig : developmentConfig return merge(generateConfig(env), config) // 合并 公共配置 和 环境配置 }
以上配置建议多看几遍熟悉熟悉,为何要这样写
const webpack = require('webpack') const path = require('path') module.exports = { mode: 'development', devtool: 'source-map', // 调试源码 devServer: { contentBase: path.join(__dirname, '../dist/'), port: 8000, hot: true, overlay: true, proxy: { '/comments': { target: 'https://m.weibo.cn', changeOrigin: true, logLevel: 'debug', headers: { Cookie: '' } } }, historyApiFallback: true }, plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NamedModulesPlugin() ] }
开发配置主要是设置跨域、开启源码调试、热更新
const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 将 css 单独打包成文件 const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin') // 压缩 css module.exports = { mode: 'production', optimization: { splitChunks: { chunks: 'all', cacheGroups: { jquery: { name: 'chunk-jquery', // 单独将 jquery 拆包 priority: 15, test: /[\\/]node_modules[\\/]jquery[\\/]/ } } } }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].css', chunkFilename: '[id].css' }), // 压缩 css new OptimizeCssAssetsPlugin({ assetNameRegExp: /\.css$/g, //一个正则表达式,指示应优化/最小化的资产的名称。提供的正则表达式针对配置中ExtractTextPlugin实例导出的文件的文件名运行,而不是源CSS文件的文件名。默认为/\.css$/g cssProcessor: require('cssnano'), //用于优化\最小化 CSS 的 CSS处理器,默认为 cssnano cssProcessorOptions: { safe: true, discardComments: { removeAll: true } }, //传递给 cssProcessor 的选项,默认为{} canPrint: true //一个布尔值,指示插件是否能够将消息打印到控制台,默认为 true }) ] } **生产配置主要是拆分代码,压缩 css**
运行 npm run dev
而且自动打开浏览器,图片和字体都出来了,打开控制台也能看到跨域成功、源码定位,由于将 devtool 设置为 'source-map',因此就会生成 map 文件,体积较大
运行 npm run build
打开 dist/index.html 文件
生产模式下跨域失败是很正常的,并且若是是 vue 项目打包完以后是没法直接打开 index.html 文件查看效果的,必需要放在服务器上,通常都是将打包后的文件放入 nginx 中,在 nginx 中配置跨域地址
还有一种配置 webpack 开发和生产环境的方式,会比较经常使用:
修改 webpack.base.conf.js
const path = require('path') const webpack = require('webpack') const HtmlWebpackPlugin = require('html-webpack-plugin') const CleanWebpackPlugin = require('clean-webpack-plugin') module.exports = { entry: { app: './src/app.js' }, output: { path: path.resolve(__dirname, '..', 'dist') }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: [ { loader: 'babel-loader' } ] }, { test: /\.(png|jpg|jpeg|gif)$/, use: [ { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', limit: 1000, // size <= 1KB outputPath: 'images/' } }, // img-loader for zip img { loader: 'image-webpack-loader', options: { // 压缩 jpg/jpeg 图片 mozjpeg: { progressive: true, quality: 65 // 压缩率 }, // 压缩 png 图片 pngquant: { quality: '65-90', speed: 4 } } } ] }, { test: /\.(eot|ttf|svg)$/, use: { loader: 'url-loader', options: { name: '[name]-[hash:5].min.[ext]', limit: 5000, // fonts file size <= 5KB, use 'base64'; else, output svg file publicPath: 'fonts/', outputPath: 'fonts/' } } } ] }, plugins: [ // 开发环境和生产环境两者均须要的插件 new HtmlWebpackPlugin({ title: 'webpack4 实战', filename: 'index.html', template: path.resolve(__dirname, '..', 'index.html'), minify: { collapseWhitespace: true } }), new webpack.ProvidePlugin({ $: 'jquery' }), new CleanWebpackPlugin() ], performance: false }
修改 webpack.dev.conf.js
const webpack = require('webpack') const merge = require('webpack-merge') const commonConfig = require('./webpack.base.conf.js') const path = require('path') const devConfig = { mode: 'development', output: { filename: '[name].js', chunkFilename: '[name].js' }, module: { rules: [ { test: /\.(sa|sc|c)ss$/, use: [ 'style-loader', { loader: 'css-loader', options: { importLoaders: 2 // 在一个 css 中引入了另外一个 css,也会执行以前两个 loader,即 postcss-loader 和 sass-loader } }, 'sass-loader', // 使用 sass-loader 将 scss 转为 css 'postcss-loader' // 使用 postcss 为 css 加上浏览器前缀 ] } ] }, devtool: 'cheap-module-eval-soure-map', devServer: { contentBase: path.join(__dirname, '../dist/'), port: 8000, hot: true, overlay: true, proxy: { '/comments': { target: 'https://m.weibo.cn', changeOrigin: true, logLevel: 'debug', headers: { Cookie: '' } } }, historyApiFallback: true }, plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NamedModulesPlugin() ] } module.exports = merge(commonConfig, devConfig)
修改 webpack.prod.conf.js
const merge = require('webpack-merge') const commonConfig = require('./webpack.base.conf.js') const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 将 css 单独打包成文件 const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin') // 压缩 css const prodConfig = { mode: 'production', output: { filename: '[name].[contenthash].js', chunkFilename: '[name].[contenthash].js' }, devtool: 'cheap-module-source-map', module: { rules: [ { test: /\.(sa|sc|c)ss$/, use: [ { loader: MiniCssExtractPlugin.loader }, { loader: 'css-loader', options: { importLoaders: 2 // 在一个 css 中引入了另外一个 css,也会执行以前两个 loader,即 postcss-loader 和 sass-loader } }, 'sass-loader', // 使用 sass-loader 将 scss 转为 css 'postcss-loader' // 使用 postcss 为 css 加上浏览器前缀 ] } ] }, optimization: { splitChunks: { chunks: 'all', cacheGroups: { jquery: { name: 'jquery', // 单独将 jquery 拆包 priority: 15, test: /[\\/]node_modules[\\/]jquery[\\/]/ }, vendors: { test: /[\\/]node_modules[\\/]/, name: 'vendors' } } } }, plugins: [ new MiniCssExtractPlugin({ filename: '[name]-[contenthash].css', chunkFilename: '[id]-[contenthash].css' }), // 压缩 css new OptimizeCssAssetsPlugin({ assetNameRegExp: /\.css$/g, //一个正则表达式,指示应优化/最小化的资产的名称。提供的正则表达式针对配置中ExtractTextPlugin实例导出的文件的文件名运行,而不是源CSS文件的文件名。默认为/\.css$/g cssProcessor: require('cssnano'), //用于优化\最小化 CSS 的 CSS处理器,默认为 cssnano cssProcessorOptions: { safe: true, discardComments: { removeAll: true } }, //传递给 cssProcessor 的选项,默认为{} canPrint: true //一个布尔值,指示插件是否能够将消息打印到控制台,默认为 true }) ] } module.exports = merge(commonConfig, prodConfig)
修改 package.json 的 script 命令
{ "scripts": { "dev": "webpack-dev-server --open --config ./build/webpack.dev.conf.js", "build": "webpack --config ./build/webpack.prod.conf.js" } }
在以前的基础又修改了一下配置,从新打包便可
新建 index.js、math.js、string.js
// index.js import * as math from './math' import * as string from './string' export default { math, string } // math.js export function add(a, b) { return a + b } export function minus(a, b) { return a - b } export function multiply(a, b) { return a * b } export function division(a, b) { return a / b } // string.js export function join(a, b) { return a + ' ' + b }
代码写完,使用 webpack 打包,安装 webpack,-D 表示 --save-dev 的简写
npm i webpack webpack-cli -D
修改 package.json
{ "name": "library", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build": "webpack" }, "keywords": [], "author": "xh", "license": "MIT", "devDependencies": { "webpack": "^4.29.6", "webpack-cli": "^3.3.0" } }
"license": "MIT"
表示彻底开源的协议,name
表示你的组件库的名称
新建 webpack.config.js 并配置
const path = require('path') module.exports = { mode: 'production', entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'library.js' } }
运行打包命令,生成 library.js 文件
这个文件就能够在项目中用了,可是咱们如今是要作一个开源库,是给别人用的,别人可能会这么用
// ES module import library from 'library' // CommonJS const library = require('library') // AMD require(['library'], function() {})
若是咱们要支持这三种形式的使用,能够在 webpack 里配置,加上 libraryTarget 参数
const path = require('path') module.exports = { mode: 'production', entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'library.js', libraryTarget: 'umd' } }
固然,若是你但愿用户还可使用 script 标签的形式引入
<script src="library.js"></script>
用户但愿能够经过 library 全局变量来使用,好比 library.math 要怎么办
能够再配置一个参数,叫 library
const path = require('path') module.exports = { mode: 'production', entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'library.js', library: 'root', // root 能够随便更换,不是固定值 libraryTarget: 'umd' } }
umd 是支持前面三种语法,可是不支持全局变量这种用法,若是配置了 library,打包以后就会将代码挂载到 root 这个全局变量上,经过 script 来引入 library,如今来打包一下,打包完以后来测试用 script 标签来引入咱们写的库
在 dist 目录下新建个 index.html 文件,并打开页面
<!DOCTYPE html> <html lang="en"> <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>自定义库</title> <script src="./library.js"></script> </head> <body></body> </html>
在控制台中输入 root,回车,就能看到咱们前面封装的函数了
libraryTarget: 'umd'
若是将 umd 改成 this
再去打包,在控制台输入 this.root 也能看到效果
libraryTarget
也能够填 window,若是在 node 环境下也可使用 global
不过通常咱们都是使用 umd
还有一种状况要注意,咱们如今写的 string.js 我以为写的很差,lodash 写的更好,我要引入这个第三方库,来代替我写的一些功能
npm install lodash
// string.js import _ from 'lodash' export function join(a, b) { return _.join([a, b], ' ') }
从新打包,体积为 70kb,由于咱们也把 lodash 也打包进去了
别人要使用咱们的库的话,须要这样 import library from 'library'
,也许别人也会用到 lodash 库,结果变成了这样:
import _ from 'lodash' import library from 'library'
最终用户的代码中就会存在两份 lodash 的代码,这种状况就要再去更改一下咱们的 webpack 配置
const path = require('path') module.exports = { mode: 'production', entry: './src/index.js', externals: ['lodash'], output: { path: path.resolve(__dirname, 'dist'), filename: 'library.js', library: 'root', libraryTarget: 'umd' } }
externals 会在打包的过程当中,若是遇到了 lodash 这个库,就不会打包进去,能够写成数组形式也能够是字符串,更改完后再次打包
能够发现咱们库里使用的 lodash 并无被打包进去,体积只有 1kb
这个时候别人再次使用咱们的 library 这个库,若是不引入 lodash,则会失败,别人在使用 library 以前要先引入 lodash
若是改成 externals: 'lodash'
,则使用的时候为,import lodash from lodash
,而不能用 _ 下划线来代替 lodash, import _ from lodash
若是要让别人使用你的库,其实就是使用你打包后的文件,须要先在 package.json,将 main: index.js 改成 main: ./dist/library.js,经过 npm 发布以前,你要确保你的库的 name 不会和别人上线的 name 冲突,改一个有特色的 name,来确保能发布成功,如 library-xh-2019
,感兴趣的能够本身去研究一下如何经过 npm 发布
{ "name": "library-xh-2019", "version": "1.0.0", "description": "", "main": "./dist/library.js", "scripts": { "build": "webpack" }, "keywords": [], "author": "xh", "license": "MIT", "devDependencies": { "webpack": "^4.29.6", "webpack-cli": "^3.3.0" }, "dependencies": { "lodash": "^4.17.11" } }