webpack学习
webpack学习
📚 Vue 学习目录
🚀 Vue.js基础学习 - 📝 Vue.js进阶 - 🔦 webpack学习
📦 Vue-CLI学习 - 📌 Vue-router学习 - 🔮 Vuex学习 - 🎀 Nuxt.js 学习
什么是webpack
webpack是一个JavaScript应用的静态模块打包工具。
需要关注的是模块和打包。grunt/gulp都可以打包,那有什么区别。
模块化
webpack可以支持前端模块化的一些方案,例如AMD、CMD、CommonJS、ES6。可以处理模块之间的依赖关系。不仅仅是js文件可以模块化,图片、css、json文件等等都可以模块化。
打包
webpack可以将模块资源打包成一个或者多个包,并且在打包过程中可以处理资源,例如压缩图片,将scss转成css,ES6语法转成ES5语法,将TypeScript转成JavaScript等等操作。grunt/gulp也可以打包。
和grunt/gulp的对比
grunt/gulp的核心是Task
- 我们可以配置一系列的task,并且定义task要处理的事务(例如ES6/TS转化,图片压缩,scss转css)
- 之后可以让grunt/gulp来执行依次这些任务,让整个流程自动化
- 所以grunt/gulp也被称为前端自动化任务管理工具
看一个gulp例子
- task将src下的js文件转化为ES5语法
- 并输入到dist文件夹中
1
2
3
4
5
6
7
8
9const gulp = require('gulp')
const babel = require('gulp-babel')
gulp.task('js'()=>
gulp.src('src/*.js')
.pipe(babel({
presets:['es2015']
}))
.pipe(gulp.dest('dist'))
);什么时候使用grunt/gulp呢?
- 如果工程依赖简单,甚至没有模块化
- 只需要进行简单的合并/压缩
- 如果模块复杂,相互依赖性强,我们需要使用webpack
grunt/glup和webpack区别
- grunt/glup更加强调的是前端自动化流程,模块化不是其核心
- webpack加强模块化开发管理,而文件压缩/合并/预处理等功能,是附带功能
webpack就是前端模块化打包工具
webpack的安装
- webpack依赖node环境。
- node环境依赖众多包,所以需要npm,npm(node packages manager)node包管理工具
- nvm是node管理工具可以自由切换node环境版本
全局安装webpack
1 | npm install webpack -g |
由于vue-cli2基于webpack3.6.0 如果要用vue-cli2的可以使用
npm install webpack@3.6.0 -g
局部安装
1 | npm install webpack --save-dev |
- 在终端执行webpack命令,使用的是全局安装。
- 当在package.json中定义了scripts时,其中包括了webpack命令,那么使用的是局部webpack
简单命令
新建一个文件夹,新建如下结构的目录:
1 | ├── dist(要发布的文件,已经处理过的) |
如上所示在src文件夹(源码文件夹),dist(要发布的文件,已经处理过的)。
新建入口js文件main.js
和mathUtils.js
,main.js
依赖mathUtils.js
。
1 | //1.新建mathUtils.js,用CommonJs规范导出 |
1 | //2.新建入口js文件main.js 导入mathUtil.js文件,并调用 |
使用webpack命令打包js文件
注意:
- webpack3使用命令:
webpack ./src/main.js ./dist/bundle.js
- webpack4使用命令:
webpack ./src/main.js -o ./dist/bundle.js
要加-o
(我使用的是webpack4) - webpack4打包成功会报
The 'mode' option has not been set
的警告、不用管、意思时要在模块上使用、这里是单文件(解决办法: https://www.cnblogs.com/wendj/p/10069882.html)
打包成功,查看dist文件夹下自动生成了一个
bundle.js
。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20//新建入口js文件main.js 导入mathUtil.js文件,并调用
const {add,mul} = __webpack_require__(1)
console.log(add(10,20))
console.log(mul(10,10))
/***/ }),
/* 1 */
/***/ (function(module, exports) {
//1.新建mathUtils.js,用CommonJs规范导出
function add(num1,num2) {
return num1+num2
}
function mul(num1,num2) {
return num1*num2
}
module.exports = {
add,mul
}内容很多,其中包含
mathUtils.js
和main.js
内容,打包成功。- webpack3使用命令:
新建一个index.html文件,导入bundle.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<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入门</title>
</head>
<body>
<!-- 3.新建一个indexhtml文件并使用 webpack ./src/main.js -o ./dist/bundle.js webpack4使用此命令 -->
<!-- 4.引用webpack打包后的js文件 -->
<script src="./dist/bundle.js"></script>
</body>
</html>可以成功打印、就说明成功了
新建一个
info.js
使用ES6的语法导出info.js
1
2
3
4
5//es6语法导出
export default {
name:'zzz',
age:24,
}main.js导入info.js
1
2
3
4
5//使用es6语法导入
import info from './info.js'
console.log(info.name)
console.log(info.age)再次使用
webpack ./src/main.js -o ./dist/bundle.js
,重新打包
打开index.html测试
总结: webpack可以帮我们打包js文件,只要指定入口文件(main.js)和输出的文件(bundle.js),不管是es6的模块化还是CommonJs的模块化,webpack都可以帮我们打包,还可以帮我们处理模块之间的依赖。
webpack配置
基本配置
如果每次都用webpack命令自己写入口文件和出口文件会很麻烦,此时我们可以使用webpack的配置。
准备工作:
新建一个文件夹,新建如下结构的目录:
1 | ├── dist(要发布的文件,已经处理过的) |
在根目录下新建一个
webpack.config.js
1
2
3
4
5
6
7
8
9
10
11//1.导入node的path包获取绝对路径,需要使用npm init初始化node包
const path = require('path')
//2.配置webpack的入口和出口
module.exports = {
entry: './src/main.js',//入口文件
output:{
path: path.resolve(__dirname, 'dist'),//动态获取打包后的文件路径,path.resolve拼接路径
filename: 'bundle.js'//打包后的文件名
}
}在根目录执行
npm init
初始化node包,因为配置文件中用到了node的path包1
npm init
看着提示的文字来选择(我记得我只输入了项目名称的、其他都是回车)
生成了
package.json
和node_modules
在根目录下载执行webpack打包
1
webpack
这样入口和出口的配置已经配置完成了,只需要使用webpack命令就行了。
使用自定义脚本(script)启动
一般来是我们使用的是
:1
2npm run dev//开发环境
npm run build//生产环境在
package.json
中的script中加上1
"build": "webpack"
使用
npm run build
1
npm run build
全局安装和局部安装
webpack有 全局安装 和 局部安装
全局安装
@xxx为对应版本
1
npm install webpack@xxx -g
局部安装
使用
npm run build
执行webpack会先从本地查找是否有webpack,如果没有会使用全局的。此时本地需要安装webapck
1
npm install webpack@xxx --save-dev
package.json
中自动加上开发时的依赖devDependencies
1
2
3"devDependencies": {
"webpack": "^xxx"
}再次使用
npm run build
,使用的是本地webpack版本。
webpack的loader
loader是webpack中一个非常核心的概念。
webpack可以将js、图片、css处理打包,但是对于webpack本身是不能处理css、图片、ES6转ES5等。
此时就需要webpack的扩展,使用对应的loader就可以。
loader使用
通过
npm
安装需要使用的loader
通过
webpack.config.js
中的modules
关键字下进行配置
大部分loader可以在webpack的官网找到对应的配置。
css文件处理
目录结构:
1 | ├── dist(要发布的文件,已经处理过的) |
将除了入口文件
(main.js)
所有js文件放在js文件夹,新建一个css
文件夹,新建一个normal.css
文件1
2
3body{
background-color: red;
}main.js
导入依赖1
require('./css/normal.css')
此时如果直接进行打包
npm run build
。提示信息很清楚,打包到css文件时报错,提示我们可能需要一个loader来处理css文件。
安装
css-loader
1
npm install --save-dev css-loader
使用css-loader
1
2
3
4
5
6
7
8
9
10
11
12
13module.exports = {
module: {
rules: [
{
test: /\.css$/,//正则表达式匹配css文件
//css-loader只负责css文件加载,不负责解析,要解析需要使用style-loader
use: [{
loader: 'css-loader'
}]//使用loader
}
]
}
}执行
npm run build
,提示打包成功,但是背景色并没有变红色,是因为css-loader只负责加载css文件,不负责解析,如果要将样式解析到dom元素中需要使用style-loader。安装使用style-loader
1
npm install --save-dev style-loader
1
2
3
4
5
6
7
8
9
10
11
12
13module: {
rules: [
{
test: /\.css$/,//正则表达式匹配css文件
//css-loader只负责css文件加载,不负责解析,要解析需要使用style-loader
use: [{
loader: 'style-loader'
}, {
loader: 'css-loader'
}]//使用loader
}
]
}webpack使用多个loader是从
右往左
解析的,所以需要将css-loader放在style-loader右边
,先加载后解析
。此时样式成加载解析到DOM元素上。
less文件处理
在css文件夹中新增一个less文件(
special.less
)1
2
3
4
5
6@fontSize:50px;//定义变量字体大小
@fontColor:orange;//定义变量字体颜色
body{
font-size: @fontSize;
color: @fontColor;
}main.js
中导入less文件模块1
2
3
4//依赖less文件
require('./css/special.less')
//向页面写入一些内容
document.writeln("hello,zykj!")安装使用
less-loader
1
npm install --save-dev less-loader less
在
webpack.config.js
中使用less-loader1
2
3
4
5
6
7
8
9
10
11
12
13
14
15module: {
rules: [
{
test: /\.less$/,//正则表达式匹配css文件
//css-loader只负责css文件加载,不负责解析,要解析需要使用style-loader
use: [{
loader: 'style-loader'
}, {
loader: 'css-loader'
}, {
loader: 'less-loader'//less文件loader
}]//使用loader
}
]
}执行
npm run build
less文件生效了,字体是orange,大小为50px。
图片文件的处理
需要准备: 两张图片,图片大小为一张8KB以下(实际大小为5KB,名称为small.jpg),一张大于8KB(实际大小为10KB,名称为big.jpg),新建一个img文件夹将两张图片放入。
修改
normal.css
样式,先使用小图片作为背景1
2
3
4body{
/* background-color: red; */
background: url("../img/small.jpg");
}此时如果直接使用
npm run build
直接打包会报错,因为css文件中引用了图片url,此时需要使用url-loader
。安装使用
url-loader
处理图片url-loader
像file loader
一样工作,但如果文件小于限制,可以返回 data URL 。1
npm install --save-dev url-loader
配置:
1
2
3
4
5
6
7
8
9
10
11{
test: /\.(png|jpg|gif|jpeg)$/,//匹配png/jpg/gif格式图片
use: [
{
loader: 'url-loader',
options: {
limit: 8192//图片小于8KB时候将图片转成base64字符串,大于8KB需要使用file-loader
}
}
]
}打包
使用
npm run build
打包后,打开index.html
。小于
limit
大小的图片地址被编译成base64格式的字符串。此时修改css文件,使用big.jpg做背景。
1
2
3
4
5body{
/* background-color: red; */
/* background: url("../img/small.jpg"); */
background: url("../img/big.jpg");
}再次打包,报错,提示未找到file-loader模块。
因为大于
limit
的图片需要file-loader
来打包。安装使用
file-loader
处理图片1
npm install --save-dev file-loader
不需要配置,因为
url-loader
超过limit的图片会直接使用file-loader
。再次打包,没有报错,打包成功,但是图片未显示。
- 当加载的图片大小
小于limit
,使用base64将图片编译成字符 - 当加载的图片大小
大于limit
,使用file-loader模块直接将big.jpg直接打包到dist文件家,文件名会使用hash值防止重复。 - 此时由于文件路径不对所以导致没有加载到图片
- 当加载的图片大小
如何使用
file-loader
,指定路径修改output属性
1
2
3
4
5output:{
path: path.resolve(__dirname, 'dist'),//动态获取打包后的文件路径,path.resolve拼接路径
filename: 'bundle.js',//打包后的文件名
publicPath: 'dist/'
},此时打包,图片正常显示
注意:一般来说,index.html最终也会打包到dist文件夹下,所以,并不需要配置publicPath,如何打包index.html请看webpack处理.vue文件。
file-loader
打包后,使用hash值做文件名太长,此时可以使用options
的一些配置。1
2
3
4options: {
limit: 8192,//图片小于8KB时候将图片转成base64字符串,大于8KB需要使用file-loader
name: 'img/[name]-[hash:8].[ext]'//img表示文件父目录,[name]表示文件名,[hash:8]表示将hash截取8位,[ext]表示后缀
}修改options,加上name属性,其中img表示文件父目录,[name]表示文件名,[hash:8]表示将hash截取8位[ext]表示后缀
再次打包
ES6语法处理
webpack打包时候ES6语法没有打包成ES5语法,如果需要将ES6打包成ES5语法,那么就需要使用babel。直接使用babel对应的loader就可以了。
安装
1 | npm install --save-dev babel-loader@7 babel-core babel-preset-es2015 |
配置
1 | { |
如果要使用
@babel/preset-env
这里需要在根目录新建一个babel
的文件exclude
排除不需要打包的文件
webpack的vue
简单安装使用vue
如果需要使用vue,必须使用npm先安装vue。
1 | npm install vue --save |
使用vue简单开发。
准备工作:在项目根目录执行npm install vue --save
,下载安装vue。
在入口文件main.js导入已安装的vue,并在index.html声明要挂载的div。在main.js加入以下代码。
1
2
3
4
5
6
7
8
9//使用vue开发
import Vue from 'vue'
const app = new Vue({
el: "#app",
data: {
message: "hello webpack and vue"
}
})修改
index.html
代码,添加1
2
3<div id="app">
<h2>{{message}}</h2>
</div>再次打包
npm run build
后打开index.html发现message并没有正确显示,打开console发现vue报错。错误提示我们,正在使用
runtime-only
构建,不能将template模板编译。runtime-only
模式,代码中不可以有任何template,因为无法解析。runtime-complier
模式,代码中可以有template,因为complier可以用于编译template。
在webpack中配置,设置指定使用
runtime-complier
模式。在
webpack.config.js
配置下修改1
2
3
4
5
6
7resolve: {
// alias:别名
alias: {
//指定vue使用vue.esm.js
'vue$':'vue/dist/vue.esm.js'
}
}重新打包,显示正确
如何分步抽取实现vue模块
- 创建vue的template和el关系
- el表示挂载DOM的挂载点
- template里面的html将替换挂载点
一般我们使用vue会开发单页面富应用(single page application),只有一个index.html,而且index.html都是简单结构。
1 |
|
第一次抽取,使用template替换
<div id="app"></div>
。修改
main.js
的vue相关代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//使用vue开发
import Vue from 'vue'
new Vue({
el: "#app",
template:`
<div>
<h2>{{message}}</h2>
<button @click='btnClick'>这是一个按钮</button>
<h2>{{name}}</h2>
</div>
`,
data: {
message: "hello webpack and vue",
name: 'zykj'
},
methods: {
btnClick(){
console.log("按钮被点击了")
}
},
})使用template模板替换挂载的id为app的div元素,此时不需要修改html代码了,只需要写template。
再次打包,显示成功。
第二次抽取,使用组件化思想替换template
在vue目录下创建
App.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<template>
<div>
{{message}}
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
message: "Hello world!"
}
}
}
</script>
<style scoped>
</style>修改main.js,vue实例中注册组件,并使用组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14//使用vue开发
import Vue from 'vue'
//导入封装vue的.vue文件
import App from './vue/App.vue'
new Vue({
el: "#app",
//使用组件
template: '<App/>',
components: {
//注册局部组件
App
}
})再次使用
npm run build
打包,打包成功,显示和使用template替换div一样。第三次抽取组件对象,封装到新的js文件,并使用模块化导入main.js
需要安装 vue-loader
1
npm install --save-dev vue-loader vue-template-compiler
此处我的vue-loader是15.9.2、会报错,此时需要降低版本
在
package.json
中webpack3.x将其修改为13.0.0、webpack4.x将其修改为14.2.4(这就是在使用webpack4遇到的坑)
1
"vue-loader": "^14.2.4"
重新安装版本
1
npm install
配置
vue-loader
、在webpack.config.js
module->rules中1
2
3
4{
test:/\.vue$/,
use: ['vue-loader']
}再次打包,打包成功,样式生效了。
组件化开发
我们使用app.vue分离了模板、行为、样式,但是不可能所有的模板和样式都在一个vue文件内,所以要用组件化。
在vue文件夹下新建一个
Cpn.vue
文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<template>
<div>
<h2 class='title'>{{name}}</h2>
</div>
</template>
<script type="text/ecmascript-6">
export default {
name: "Cpn",
data() {
return {
name: "组件名字是Cpn"
};
}
};
</script>
<style scoped>
.title {
color: red;
}
</style>将Cpn.vue组件导入到App.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35<template>
<div>
<h2 class='title'>{{message}}</h2>
<button @click="btnClick">按钮</button>
<h2>{{name}}</h2>
<!-- 使用Cpn组件 -->
<Cpn/>
</div>
</template>
<script type="text/ecmascript-6">
//导入Cpn组件
import Cpn from './Cpn.vue'
export default {
name: "App",
data() {
return {
message: "hello webpack",
name: "zzz"
};
},
methods: {
btnclick() {}
},
components: {
Cpn //注册Cpn组件
}
};
</script>
<style scoped>
.title {
color: green;
}
</style>再次打包,打开index.html,cpn组件的内容显示基于此,一个vue文件可以依赖导入很多vue文件,组成一个单页面富应用。
如果你在使用ES6语法导入模块时候想要简写的时候,例如这样省略
.vue
后缀1
import Cpn from './Cpn'
可以在
webpack.config.js
中配置:1
2
3
4
5
6
7
8
9resolve: {
//导入模块简写省略指定后缀如 import App from './vue/App'
extensions: ['.js', '.css', '.vue'],
// alias:别名
alias: {
//指定vue使用vue.esm.js
'vue$':'vue/dist/vue.esm.js'
}
}
webpack的plugin
plugin插件用于扩展webpack的功能的扩展,例如打包时候优化,文件压缩。
loader和plugin的区别
loader主要用于转化某些类型的模块,是一个转化器。
plugin主要是对webpack的本身的扩展,是一个扩展器。
plugin的使用过程
步骤一:通过npm安装需要使用的plugins(某些webpack已经内置的插件不需要在安装)
步骤二:在
webpack.config.js
中的plugins中配置插件。
添加版权的Plugin
BannerPlugin
插件是属于webpack自带的插件可以添加版权信息。
自带的插件无需安装,直接配置。
先获取webpack的对象,在配置BannerPlugin插件。
1 | //获取webpack |
打包后,查看bundle.js第一行
1 | /*! 最终解释权归zykj所有 */ |
多了一行我们自定义的版权声明注释。
打包html的plugin
之前我们的index.html文件都是存放在根目录下的。
在正式发布项目的时候发布的是dist文件夹的内容,但是dist文件夹是没有index.html文件的,那么打包就没有意义了。
所以我们需要将index.html也打包到dist文件夹中,这就需要使用HtmlWebpackPlugin
插件了。
HtmlWebpackPlugin
:自动生成一个index.html文件(指定模板)
将打包的js文件,自动同script标签插入到body中
首先需要安装HtmlWebpackPlugin
插件
1 | npm install html-webpack-plugin --save-dev |
使用插件,修改webpack.config.js文件中的plugins部分
1 | //获取htmlWebpackPlugin对象 |
注意:
- template表示根据哪个模板来生成index.html
- 需要删除
output
中添加的publicPath
属性,否则插入的script标签的src可能有误
再次打包,打开dist文件夹,多了一个index.html
自动加入了script引入了bundle.js。
压缩打包代码插件
webpack3.x
:
uglifyjs-webpack-plugin是第三方插件,如果是vuecli2需要指定版本1.1.1。
安装:
1 | npm install uglifyjs-webpack-plugin@1.1.1 --save-dev |
配置plugin
1 | //获取uglifyjs-webpack-plugin对象 |
打包过后,打开bundle.js,发现已经压缩了,此时版权声明被删除了。
webpack高版本
:自带了压缩插件。
以webpack4.x为例、配置package.json
1 | "scripts": { |
mode development
: 不会自动压缩mode production
: 同样会调用UglifyjsPlugin
插件、会自动压缩
运行 npm run dev
不会自动压缩
webpack搭建本地服务器
webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js搭建,内部使用了express框架,可以实现热启动。
不过这是一个单独的模块,在webpack中使用之前需要先安装:
1 | npm install --save-dev webpack-dev-server@2.9.1 |
devServer也是webpack中一个选项,选项本省可以设置一些属性:
contentBase
:为哪个文件夹提供本地服务,默认是根文件夹,这里我们需要改成./dist
port
:端口号inline
:页面实时刷新historyApiFallback
:在SPA(单页面富应用)页面中,依赖HTML5的history模式
修改webpack.config.js的文件配置
1 | //配置webpack的入口和出口 |
配置package.json
的script:
1 | "dev": "webpack-dev-server --open" |
--open
: 表示直接打开浏览器
启动服务器
1 | npm run dev |
启动成功,自动打开浏览器,发现在本地指定端口启动了,此时你修改src文件内容,会热修改。
服务器启动在内存中。
开发调试时候最好不要使用压缩js文件的插件,不易调试。
webpack的配置文件分离
webpack.config.js
文件中有些是开发时候需要配置,有些事生产环境发布编译需要的配置,比如搭建本地服务器的devServer配置就是开发时配置,接下来我们分析如何分离配置文件。
目录结构:
1 | ├── dist(要发布的文件,已经处理过的) |
在根目录下新建一个build
的文件夹,新建配置文件。
1 | //1.导入node的path包获取绝对路径,需要使用npm init初始化node包 |
1 | module.exports = { |
1 | const uglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin') |
此时我们将webpack.config.js
文件分成了三个部分,公共部分、开发部分、构建发布的部分。
如果此时是dev环境,我们只需要使用
base.config.js
+dev.config.js
的内容如果此时是生产发布构建的环境,我们只需要使用
base.config.js
+prod.config.js
的内容
要将两个文件内容合并需要使用webpack-merge
插件,安装webpack-merge
。
1 | npm install webpack-merge --save-dev |
合并内容都是将base.config.js
的内容合并到dev或者prod的文件中,修改dev.config.js
和prod.config.js
文件。
1 | //导入webpack-merge对象 |
1 | const uglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin') |
此时我们使用三个文件构成了配置文件,此时在不同环境使用不同的配置文件,但是webpack不知道我们新配置文件,此时我们需要在package.json中的script指定要使用的配置文件。
1 | "scripts": { |
此时使用npm run build
打包文件,dist文件并不在根目录下,因为我们在base.config.js
中配置的出口文件使用的是当前文件的路径,即打包的根路径是配置文件的当前路径,也就是build文件夹。
1 | entry: './src/main.js',//入口文件 |
注意:__dirname是当前文件路径,path.resolve拼接路径,所以在当前路径下创建了一个dist文件夹。
此时修改output属性:
1 | output:{ |
使用
../dist
,在当前目录的上级目录创建dist文件夹