6. Vue计算属性(computed)和侦听器(watch)
7. webpack(module bundler,模块打包器)
9. webpack常用转换器(loader)与插件(plugin)
11. 模块管理规范(ES6、CommonsJs、AMD、CMD)
webpack主要用来处理js代码,并且webpack会自动处理js之间相关的依赖。单在实际开发中不仅仅有基本的js代码,也需要加载css、图片,也包括一些高级的将ES6转成ES5代码,将TypeScript转成ES5代码,将scss、less转成css,将.jsx、.vue文件转成js文件等
webpack默认打开包除了js文件以外的所有文件,需要装不同的文件类型都需要安装对应的loader才可以加载,否则会报错
(1)通过npm安装需要使用的loader
(2)在webpack.config.js中的modules关键字下进行配置
module: {
rules: [{
test: /.css$/,
use: [{
loader: "style-loader"
},
{
loader: "css-loader"
}
]
},
{
test: /.less$/,
use: [{
loader: "style-loader"
},
{
loader: "css-loader"
},
{
loader: "less-loader"
}
]
},
{
test: /.scss$/,
use: [{
loader: "style-loader"
},
{
loader: "css-loader"
},
{
loader: "sass-loader"
}
]
},
{
test: /.(png|jpg|gif|bmp|webp)$/,
use: [{
loader: 'url-loader?limit=14240&name=[hash:8]-[name].[ext]',
}]
},
{
test: /.(eot|ttf|woff|woff2|svg)$/,
use: 'url-loader',
},
{
test: /.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
}
},
{
test: /.vue$/,
use: {
loader: 'vue-loader',
}
}
]
}
npm install css-loader style-loader --save-dev
cnpm install css-loader style-loader --save-dev
yarn add css-loader style-loader --save-dev(推荐)
如果没有安装yarn,详见:npm与yarn详细
在src/css目录中新建index.css
#first {
border: 1px solid red;
}
请不要再html中引入css,避免二次http请求
效果:获取css目录中的index.css文件
// ES6的import语法可以导入js和css
import './css/index.css'
// CommonJS
require(./css/index.css)
module下参数 | 描述 |
---|---|
rules | loader装载机使用规则 |
css-loader | 将模块导出的内容作为样式并添加到DOM中 |
style-loader | 加载CSS文件并解析import的CSS文件,最终返回CSS代码 |
注意:style-loader需要放在css-loader的前面。因为多个loader加载顺序是从右到左 |
// 首先要引入node.js中path模块,用于处理文件与目录的路径
const path = require('path');
// const命令声明一个只读的常量,一旦声明,值不可以改变,改变会报错;只声明不赋值也会报错;
// const常量存储的是一个不可以变化的变量
module.exports = {
entry:'./src/./js/main.js', // 指定入口文件
output:{
path: path.resolve(__dirname, './dist/js'), // 指定出口文件的路径目录
filename: 'bulid.js' // 制定出口文件的名称
},
module:{
rules:[
// 在webpack2中,loaders被替换成了rules,其实就是loader的规则
{
// “/\.css$/”:相当于一个正则表达式
test: /\.css$/,
// 多个loader的加载顺序是从右到左的
use: [ 'style-loader', 'css-loader' ]
// test 说明了当前loader能处理那些类型的文件
// use 则指定了loader的类型
// 注意:数组中的loader不能省略扩展名
}
]
}
}
如果配置了scripts
npm run build
yarn run build
将Less编译为CSS的loader
npm install less less-loader --save-dev
cnpm install less less-loader --save-dev
yarn add less less-loader --save-dev
index.less
@fontSize: 50px;
@fontColor : white;
#res {
border:1px dashed blue;
}
// 获取less下index.less文件
import './less/index.less'
module: {
rules: [
{
test: /\.less$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader" },
{ loader: "less-loader" }
]
}
]
},
注意:use下使用的是对象,即可额外传参
加载Sass/SCSS文件并将它们编译为CSS
npm install sass-loader sass --save-dev
cnpm install sass-loader sass --save-dev
yarn add sass-loader sass --save-dev
$body-color: red;
body {
color: $body-color;
}
import "./css/index.scss"
module.exports = {
module: {
rules: [
{
test: /\.s[ac]ss$/i,
use: [
// 将js字符串生成为style节点
"style-loader",
// 将css转化成 CommonJS 模块
"css-loader",
// 将sass编译成css
"sass-loader",
],
},
],
}
}
因为在css中使用图片或字体文件时会引入外部资源,而webpack进行打包时默认不支持这些外部资源的打包,所以需要借助 url-loader
npm install url-loader --save-dev
cnpm install url-loader --save-dev
yarn add url-loader --save-dev
css/index.css
#bg {
width: 200px;
height: 200px;
background: url('../imgs/bxg.jpg');
}
import img from './img/image.jpeg';
import './css/index.css'
limit: 8192
限制图片的最大大小,如果超过了最大限制将会以原图片打包,如果小于限制则会通过base64进行转码
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|jpeg|gif)$/i,
use: [
{
loader: 'url-loader',
options: {
// limit默认无限制
// 当加载的图片,小于limit,图片将会被编译成base64字符串形式
// 当加载的图片,大于limit时,需要使用file-loader模块进行加载(转码)
limit: 8192,
name: 'img/[name][hash:8].[ext]'
}
}
]
}
]
}
};
注意:在css中引入外部的图片或字体都需要用url-loader来加载,建议分开匹配,方便管理
将文件上的/file-loader解析为url,然后将文件发送到输出目录
在实际开发中,可能对图片打包的名字有一定的要求。比如:将所有的图片放在一个文件夹中,根上图片原来的名称,同时也要防止重复,所以可以在options中添加占位符等
占位符 | 描述 |
---|---|
img | 文件打包到的文件夹 |
[name] | 获取文件原来的名称 |
[hash:8] | 防止图片名称冲突,8位hash值 |
[ext] | 图片原来的扩展名 |
占位符
占位符 | 类型 | 描述 |
---|---|---|
[name] | String | 文件/资源的基本名称。默认值:file.basename |
[ext] | String | 目标文件/资源的文件扩展名。默认值:file.extname |
[path] | String | 资源相对于webpack / config的路径context。默认值:file.directory |
[folder] | String | 资源的文件夹在。默认值:file.folder |
[query] | String | 资源查询,即?foo=bar。默认值:file.query |
[emoji] | String | 随机表情符号表示形式content。默认值:undefined |
[emoji:<length>] | String | 与[emoji]相同,但可自定义数量的表情符号。默认值:undefined |
[hash] | String | 指定用于对文件内容进行哈希处理的哈希方法。默认值:md4 |
[contenthash] | String | 指定用于对文件内容进行哈希处理的哈希方法。默认值:md4 |
digestType | String | 哈希函数应使用的摘要。有效值包括:base26,base32,base36,base49,base52,base58,base62,base64和hex。默认值:‘hex’ |
hashType | String | hash函数应使用的哈希类型。有效值包括:md4,md5,sha1,sha256,和sha512。默认值:‘md4’ |
length | Number | 用户还可以为计算的哈希指定长度。默认值:undefined |
[N] | String | 通过将当前文件名与进行匹配而获得的第n个匹配项regExp。默认值:undefined |
npm install file-loader --save-dev
cnpm install file-loader --save-dev
yarn add file-loader --save-dev
import img from './file/file.png'
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader'
}
]
}
]
}
}
然后webpack通过首选方法运行。这将file.png作为输出目录中的文件发出(使用指定的命名约定,如果指定了这样做的选项),并返回文件的公共URI
默认情况下,结果文件的文件名是文件内容的哈希值,带有所需资源的原始扩展名
在项目中如果用到了高级的ES6语法,例如class关键字,webpack默认无法进行打包,需要借助babel将高级语法转译为ES5语法之后再进行打包
babel核心、babel-loader以及babel的插件
npm install -D babel-loader @babel/core @babel/preset-env
cnpm install -D babel-loader @babel/core @babel/preset-env
yarn add -D babel-loader @babel/core @babel/preset-env
-D相当于–save-dev
webpack.config.js
module: {
rules: [
{
test: /\.m?js$/,
// include:包含
// exclude:排除
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
}
src下配置.babelrc文件
配置babel,需要在项目根目录新建.babelrc文件
{
"presets": [
"es2015",
"env",
"stage-0"
],
"plugins": [
"transform-runtime"
]
}
webpack.config.js
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
}
]
}
Vue Loader是一个webpack的loader,它允许以一种名为单文件组件(SFCs)的格式撰写Vue组件
vue.runtime.js(runtime-only) | 代码中,不可以有任何的template |
---|---|
vue.runtime.esm.js(runtime + compiler) | 代码中,可以有template。由vue-template-compiler编译template |
<template>
<div class="example">{{ msg }}</div>
</template>
<script>
export default {
data () {
return {
msg: 'Hello world!'
}
}
}
</script>
<style>
.example {
color: red;
}
</style>
Vue Loader提供了很多酷炫的特性:
(1)允许为Vue组件的每个部分使用其它的webpack loader
例如:在 <style> 的部分使用 Sass 和在 <template> 的部分使用 Pug;
(2)允许在一个.vue文件中使用自定义块,并对其运用自定义的loader链;
(3)使用webpack loader将<style>和<template>中引用的资源当作模块依赖来处理
(4)为每个组件模拟出scoped CSS
(5)在开发过程中使用热重载来保持状态
简而言之,webpack和Vue Loader的结合提供了一个现代、灵活且极其强大的前端工作流,来帮助撰写Vue.js应用
如果不想手动设置webpack,推荐使用Vue CLI直接创建一个项目的脚手架。通过Vue CLI创建的项目会针对多数常见的开发需求进行预先配置,做到开箱即用
npm install vue-loader vue-template-compiler -D
每个vue包的新版本发布时,一个相应版本的vue-template-compiler也会随之发布。编译器的版本必须和基本的vue包保持同步,这样vue-loader就会生成兼容运行时的代码。这意味着每次升级项目中的vue包时,也应该匹配升级vue-template-compiler
Vue Loader的配置和其它的loader不太一样。除了通过一条规则(rules)将vue-loader应用到所有扩展名为“.vue”的文件上之外,还需要在webpack 配置中添加Vue Loader的插件
注意:必须配置VueLoaderPlugin(不需要额外装包,该插件在vue-loader中自带)
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
module: {
rules: [
// ... 其它规则
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
plugins: [
// 请确保引入这个插件!
new VueLoaderPlugin()
]
}
配置完毕后就可以在main.js中引入.vue文件,使用render函数等方式进行渲染了
方式①:main.js
new Vue({
el: '#app',
template: '<div>\n' +
'<h1>{{ message }}</h1>\n' +
'<h4 @click="btnClick"></h4>\n' +
'</div>',
data() {
return {
message: "info",
name: "code"
}
},
methods: {
btnClick() {
console.log("abc!");
}
}
})
方式②:main.js
main.js
const App = {
template: '<div>\n' +
'<h1>{{ message }}</h1>\n' +
'<h4 @click="btnClick"></h4>\n' +
'</div>',
data() {
return {
message: "info",
name: "code"
}
},
methods: {
btnClick() {
console.log("abc!");
}
}
}
main.js
new Vue({
el: '#app',
template: '<App/>',
components: {
App
}
})
方式③:common.vue定义组件
common.vue
<template>
<div>
<h1>{{ message }}</h1>
<h4 @click="btnClick"></h4>
</div>
</template>
<script>
export default {
name: 'common',
data() {
return {
message: "info",
name: "code"
}
},
methods: {
btnClick() {
console.log("abc!");
}
}
}
</script>
<style scoped>
</style>
main.js
import App from './App.vue'
new Vue({
el: '#app',
template: '<App/>',
components: {
App
}
})
最后一步:使用组件
App.vue
<template>
<div>
<app></app>
</div>
</template>
<script>
import App from './App.vue'
export default {
name: "start",
components :{
App
}
}
</script>
<style scoped>
</style>
render函数跟template一样都是创建html模板的,但是有些场景中用template实现起来代码冗长繁琐而且有大量重复,这时候就可以用render函数
函数:render
类型:(createElement: () => VNode) => VNode
说明:字符串模板的代替方案,允许发挥JavaScript最大的编程能力。该渲染函数接收一个createElement方法作为第一个参数用来创建VNode(虚拟DOM)
如果组件是一个函数组件,渲染函数还会接收一个额外的context参数,为没有实例的函数组件提供上下文信息
Vue选项中的render函数若存在,则Vue构造函数不会从template选项或通过el选项指定的挂载元素中提取出的HTML模板编译渲染函数
createElement参数(三个)
返回值:VNode(虚拟DOM)
createElement(
// 类型:{String | Object | Function}
// 说明:一个HTML标签名、组件选项对象,或者resolve了上述任何一种的一个async函数。必填
'div',
// 类型:{Object}
// 说明:一个与模板中 attribute对应的数据对象。可选
{
},
// {String | Array}
// 子级虚拟节点(VNodes),由“createElement()”构建而成,也可以使用字符串来生成“文本虚拟节点”。可选
[
'先写一些文字',
createElement('h1', '一则头条'),
createElement(MyComponent, {
props: {
someProp: 'foobar'
}
})
]
)
(1)plugin是插件的意思,通常是用于对某个现有的架构或功能进行扩展
(2)webpack中的插件,就是对webpack现有功能的各种扩展,比如:打包优化,文件压缩等等
①:通过npm安装需要使用的plugins(某些webpack已经内置的插件不需要安装)
②:在webpack.config.js中的plugins中配置插件
loader:用于转换某些类型的模块,是一个转换器
plugin:插件。它是对webpack的本身的扩展,是一个扩展器
npm init -y
npm install jquery
│ package-lock.json
│ package.json
└─src
│ index.html // 自己创建
│ main.js // 自己创建
// ES6模块化语法导入jQuery
import $ from 'jquery'
// 此处为案例js代码,可以忽略(隔行变色)
$(function () {
$('ul>li:odd').css('backgroundColor', 'pink')
$('ul>li:even').css('backgroundColor', 'purple')
})
ES6模块化语法
语句 | 说明 |
---|---|
import | 导入的库对象的变量名 |
from | 可以写具体的路径,也可以写包名;如果写的是包名就会去/node_modules中查找 |
在index.html直接引入src/main.js无法再浏览器中运行,因为浏览器不支持import语法;所以需要webpack进行打包输出一个bundle.js,执行以下命令:
参数 | 说明 |
---|---|
-d | 指定打包模式为development(开发),表示不压缩 |
-p | 指定打包模式为production(线上),表示压缩 |
通过“-d”参数可以指定打包模式为development,表示不压缩;-p表示production模式,并压缩 |
webpack ./src/main.js -o ./dist/bundle.js -d
上面命令会在自动生成一个dist文件并且会生成一个bundle.js文件,webpack将jQuery文件和自己写的js代码的代码编译到bundle.js中
webpack.config.js配置文件(需要放在项目的根目录下使用)
const path = require('path')
module.exports = {
// 入口。可以为相对路径,当然绝对路径也没错
entry: './src/main.js',
output: { // 输出配置
path: path.join(__dirname, './dist'), // 输出的目录
filename: 'bundle.js' // 输出的文件名
},
mode: 'production' // 打包的模式:production | development
}
然后执行命令,因为在webpack.config.js中配置了入口和出口了,直接执行如下命令即可
webpack
当每次修改或者新增一个js文件的时候,就会重新执行一下webpack 命令进行编译,这种方式非常的麻烦,这样整个项目下来岂不是要执行百万次。使用webpack命令相关的参数,就避免这个情况
命令 | 描述 |
---|---|
webpack --config XXX.js | 使用另一份配置文件(比如webpack.config2.js)来打包 |
webpack --watch | 监听变动并自动打包 |
webpack -p | 压缩混淆脚本,这个非常非常重要 |
webpack -d | 生成map映射文件,告知哪些模块被最终打包到哪里了其中的 |
webpack --progress | 显示进度条 |
webpack --color | 添加颜色 |
参数 | 说明 |
---|---|
-p | 一个未压缩的 700kb 的文件,压缩后直接降到 180kb (主要是样式这块一句就独占一行脚本,导致未压缩脚本变得很大) |
–watch | 监听文件是否有改变,有改变就会重新编译有改变的文件 |
打包文件头部添加注释,相当于Spring Boot启动时的banner
注意:webpack-banner-plugin是webpack默认包含的不需要安装
const webpack = require('webpack')
module.exports = {
plugins: [
new webpack.BannerPlugin("(c) 最终版权归---所有")
]
}
webpack-dev-server提供了一个简单的网络服务器,并具有实时重新加载(实时重新加载)功能
webpack-dev-server是一个小型的node.js Express服务器,它使用webpack-dev-middleware中间件来为通过webpack打包生成的资源文件提供Web服务。它还有一个通过Socket.IO连接着webpack-dev-server服务器的小型运行时程序。webpack-dev-server发送关于编译状态的消息到客户端,客户端根据消息作出响应
webpack-dev-server在编译之后将不会写入到任何输出文件。另外将捆绑文件保留在内存中,然后将它们服务到server中,就好像它们是挂载在server根路径上的真实文件一样。如果页面希望在其他不同路径中找到捆绑文件,则可以通过dev server配置中的publicPath选项进行修改
执行npm run build才会编译存储在磁盘中
npm install webpack-dev-server webpack webpack-cli -D
npm install webpack-dev-server -D
yarn add webpack-dev-server -D
修改配置文件,告知dev服务器,从什么位置查找文件
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
print: './src/print.js'
},
devtool: 'inline-source-map',
devServer: { // +
contentBase: './dist', // +
hot: true // +
}, // +
plugins: [
new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }),
new HtmlWebpackPlugin({
title: 'Development',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
}
}
以上配置通知webpack-dev-server,将dist目录下的文件服务到localhost:8080下
devServer是webpack中的一个选项,选项本身可以设置如下属性
选项 | 描述 |
---|---|
contenBase | 为哪个文件夹提供本地服务,默认是根文件夹 |
port | 端口号 |
inline | 页面实时刷新 |
historyApiFallback | 在SPA页面中,依赖HTML5的history模式 |
hot | 热模块加载 |
open | 自动打开浏览器 |
注意:以上选择可以在devServer中直接配置属性,也可以在scripts中配置命令,能够到达相同的效果;需要加上“–”号。例:–hot |
在script节点下添加一个dev脚本
①:简单脚本
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server"
},
②:设置webpack-dev-server的端口号,dev脚本命令后面加上参数“--port”
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server --port 3000"
},
③:设置服务器的根路径、热模块替换、自动打开浏览器
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server --hot --open --contentBase ./src --port 3000"
// --contenBase ./src:如果配置了,webpack.config.js就不用配置contentBase了
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack serve --hot--open --port 8000"
// serve:将资源作为服务器的可访问文件,相当于:-dev-server
},
注意:默认情况下webpack-dev-server会将入口的文件打包输出到项目的根目录, 并且不会在物理磁盘中保存,只在内存中存放,方便开发调试,请不要在json文件中添加注释
现在bundle.js已经是托管到内存中了,如果需要将HTML也托管到内存中,需要借助一个插件: html-webpack-plugin
在命令行中运行npm dev/npm run dev,会看到浏览器自动加载页面。如果更改任何源文件并保存它们,则Web服务器将在编译代码后自动重新加载
npm run dev
解释:当执行npm run dev/npm dev时,会到package.json下去找到scripts#dev,并执行后面的命令
脚本执行流程
package.json中的scripts的脚本在执行时,会按照一定的顺序寻找命令对象的位置
(1)首先,会找本地(项目下)的node_modules/.bin路径中对应的命令
(2)全局环境变量中寻找
devServer.publicPath & devServer.contentBase
参数 | 描述 |
---|---|
devServer.contentBase | 告诉服务器从哪里提供内容。只有在想要提供静态文件时才需要 |
devServer.publicPath | 用于确定应该从哪里提供 bundle,并且此选项优先 |
在开发阶段,借用devServer启动一个开发服务器进行开发,这里也会配置一个publicPath,这里的publicPath路径下的打包文件可以在浏览器中访问。而静态资源仍然使用output.publicPath。
webpack-dev-server打包的内容是放在内存中的,这些打包后的资源对外的的根目录就是publicPath,换句话说,这里设置的是打包后资源存放的位置
devServer的publicPath:
const publicPath = '/dist/'
// 则启动devServer后index.html的位置
const htmlPath = `${pablicPath}index.html`
// 包的位置
cosnt mainJsPath = `${pablicPath}main.js`
以上可以直接通过http://lcoalhost:8080/dist/main.js访问到
访问: http://localhost:8080/webpack-dev-server,可以得到devServer启动后的资源访问路径
在真实发布项目时,发布的是dist(出口)文件夹中的内容,但是dist文件夹中如果没有index.html文件,那么打包的js等文件也就没有意义了;所以需要将index.html文件打包到dist文件夹中,这时候就需要用到HtmlWebpackPlugin插件
创建HTML文件来服务您的捆绑软件
用于将css和js添加到html模版中,其中template和filename会受到路径的影响,从源码中可以看出
①:自动升一个index.html文件(可以指定模板来生成)
②:将打包的js文件,自动通过script标签插入到body中
使用插件后,内存中的index.html会自动添加bundle.js的引入,不需要自己手动添加,这样减少了html的代码量
npm install --save-dev html-webpack-plugin
yarn add --save-dev html-webpack-plugin
注意:需要删除在output中添加的publicPath属性,否则插入的script标签中src可能会有问题
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
①:不使用模板,即index.html还需要引入编译后的文件
plugins: [new HtmlWebpackPlugin()]
②:template表示根据什么模板来生成index.html
plugins: [
new HtmlWebpackPlugin({
template: 'index.html'
})
new HTMLWebpackPlugin({
// 源文件
template: path.join(__dirname, './src/index.html'),
// 输出在服务器根目录的文件名, 文件存放在内存中, 不会在磁盘上显示
filename: 'index.html'
})
]
};
template:用于定义模版文件的路径
源码:
this.options.template = this.getFullTemplatePath(this.options.template, compiler.context);
因此template只有定义在webpack的context下才会被识别,webpack context的默认值为process.cwd(),既运行node命令时所在的文件夹的绝对路径
filename:输出的HTML文件名,默认为index.html,可以直接配置带有子目录
源码:
this.options.filename = path.relative(compiler.options.output.path, filename);
所以filename的路径是相对于output.path的,而在webpack-dev-server中,则是相对于webpack-dev-server配置的publicPath
如果webpack-dev-server的publicPath和output.publicPath不一致,在使用html-webpack-plugin可能会导致引用静态资源失败,因为在devServer中仍然以output.publicPath引用静态资源,和webpack-dev-server的提供的资源访问路径不一致,从而无法正常访问。
有一种情况除外,就是output.publicPath是相对路径,这时候可以访问本地资源;所以一般情况下都要保证devServer中的publicPath与output.publicPath保持一致
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app"></div>
<script src="./dist/build.js"></script>
// 使用上面②方式,js等引入可省略
</body>
</html>
uglify-js用于压缩JavaScript
注意:要求的最小版本为Node v6.9.0和Webpack v4.0.0版本
npm install uglifyjs-webpack-plugin --save-dev
yarn add uglifyjs-webpack-plugin -D
在webpack.config.js配置
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
optimization: {
minimizer: [new UglifyJsPlugin()],
},
};
紧接着通过首选方式运行webpack
开发(开发环境)和生产(生产环境)这两个环境下的构建目标存在着巨大差异
开发环境中:需要强大的源地图和一个有着重加载(实时重新加载)或热模块更换(热模块替换)能力的本地服务器
生产环境中:目标则转移到其他方面,关注点在于压缩包,更轻量的源地图,资源优化等,通过这些优化方式改善加载时间。通常建议为每个环境编写彼此独立的webpack配置
通用配置:虽然将生产环境和开发环境做了略微区分,但是,还是会遵循不重复原则,保留一个“common(通用)”配置。配置合并在一起,将使用一个称为webpack-merge的工具。此工具会引用“common”配置,因此不必再在环境特定的(特定于环境)的配置中编写重复代码
npm install --save-dev webpack-merge
yarn add --save-dev webpack-merge
common.cofing.js(公共)
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js'
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
// 对于CleanWebpackPlugin的v2 versions以下版本,使用new CleanWebpackPlugin(['dsit/*'])
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'Production'
}),
],
}
dev.config.js(开发)
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
contentBase: './dist'
}
})
prod.config.js(生产)
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'production'
});
把scripts的命令重新指向指向新配置文件(默认:webpack.config.js)
package.json(Scripts)
"scripts": {
// "start": "webpack serve --open",改为↓
"start": "webpack serve --open --config webpack.dev.js",
// "build": "webpack",改为↓
"build": "webpack --config webpack.prod.js"
}
解释:让script#start(npm start)中webpack-dev-server,使用webpack.dev.js配置文件,而让script#build(npm run build)使用webpack.prod.js配置文件
许多library通过与process.env.NODE_ENV环境变量关联,以决定library中应该引用哪些内容。例如,当process.env.NODE_ENV没有被设置为’production’时,某些library为了使调试变得容易,可能会添加额外的log(日志记录)和test(测试)功能。并且,在使用process.env.NODE_ENV === 'production’时,一些library可能针对具体用户的环境,删除或添加一些重要代码,以进行代码执行方面的优化。从webpack v4开始,指定mode会自动地配置DefinePlugin
提供mode配置选项,告知webpack使用相应模式的内置优化
语法 => mode : string
值:string = ‘none’ | ‘development’ | ‘production’
选项 | 描述 |
---|---|
development | 会将DefinePlugin中process.env.NODE_ENV的值设置为development,为模块和chunk启用有效的名 |
production | 会将DefinePlugin中process.env.NODE_ENV的值设置为production。为模块和chunk启用确定性的混淆名称,FlagDependencyUsagePlugin、FlagIncludedChunksPlugin、ModuleConcatenationPlugin、NoEmitOnErrorsPlugin和TerserPlugin |
none | 不使用任何默认优化选项 |
如果没有设置,webpack会给mode的默认值设置为production | |
注意:设置NODE_ENV并不会自动地设置mode |
用法
1.只需在配置对象(module.exports)中提供mode选项
module.exports = {
mode: 'development'
};
2.从CLI参数中传递
webpack --mode=development
DefinePlugin允许在编译时创建配置的全局常量,这在需要区分开发模式与生产模式进行不同的操作时,非常有用(webpack自带)
例如:如果想在开发构建中进行日志记录,而不在生产构建中进行,就可以定义一个全局常量去判断是否记录日志。这就是DefinePlugin的发光之处,设置好它,就可以忘掉开发环境和生产环境的构建规则
传递给DefinePlugin的每个键都是一个标识符或多个以“.”连接的标识符
(1)如果该平均值字符串,则被作为代码片段来使用
(2)如果该值不是字符串,则将被转换成字符串(包括函数方法)
(3)如果值是一个对象,则它所有的键将使用相同方法定义
(4)如果键添加typeof作为前缀,它会被定义为typeof调用
定义全局常量
new webpack.DefinePlugin({
PRODUCTION: JSON.stringify(true),
VERSION: JSON.stringify('5fa3b9'),
BROWSER_SUPPORTS_HTML5: true,
TWO: '1+1',
'typeof window': JSON.stringify('object'),
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
});
这些值将内联到代码中,从而允许通过代码压缩来删除冗余的条件判断
使用常量
console.log('Running App version ' + VERSION);
if(!BROWSER_SUPPORTS_HTML5) require('html5shiv');
注意:在定义process值时,两种方式比较
方式①:
'process.env.NODE_ENV': JSON.stringify('production')
方式②:
process: {
env: {
NODE_ENV: JSON.stringify('production')
}
}
方式①更好,方式②会覆盖process对象,这可能会破坏与某些模块的兼容性,因为这些模块会在过程对象上定义其他值
也就是说,使用方式②定义process,第二次定义值的时候会将第一次覆盖掉
注意:由于本插件会直接替换文本,因此提供的值必须在串联本身中再包含一个实际的引号。通常,可以使用类似’“production”'这样的替换引号,或者直接用JSON.stringify(‘production’)
if (!PRODUCTION) {
console.log('Debug info');
}
if (PRODUCTION) {
console.log('Production log');
}
非官方webpack压缩过的代码
if (!true) {
console.log('Debug info');
}
if (true) {
console.log('Production log');
}
经过压缩后
console.log('Production log');
使用场景
new webpack.DefinePlugin({
// 在生产/开发内置中使用功能标志可以启用/添加项目的不同特性
'NICE_FEATURE': JSON.stringify(true),
'EXPERIMENTAL_FEATURE': JSON.stringify(false)
// 服务网址:在生产或开发建造中使用不同的服务URL
'SERVICE_URL': JSON.stringify('https://dev.example.com')
})
运行时值通过runtimeValue
可以使用依赖于文件的值定义变量,并且当这些文件在文件系统中更改时将被重新评估。这意味着当此类监视的文件更改时,webpack将重建
function (getterFunction, [string]) => getterFunction()
第一个参数 webpack.DefinePlugin.runtimeValue是function,应返回要分配给定义的值
第二个参数 要监视的文件路径数组。通过true而不是[string]此处将模块标记为不可缓存
const fileDep = path.resolve(__dirname, 'sample.txt');
new webpack.DefinePlugin({
BUILT_AT: webpack.DefinePlugin.runtimeValue(Date.now, [fileDep])
})
BUILT_AT值应为“sample.txt”是文件系统中的最后更新时间。例如:1597953013291