一、webpack安装
- 全局安装webpack
npm install webpack@3.6.0 -g
- 局部安装webpack
cd 对应目录
npm install webpack --save-dev
--save-dev
是开发时依赖,项目打包后不需要继续使用。
- 全局安装后为何需要局部安装?
-
在终端直接执行webpack命令,使用的是全局安装的webpack。
-
在package.json中定义了scripts时,其中包含的webpack命令使用的是局部webpack。
二、webpack的起步
2.1 为什么使用webpack?
在js文件中使用了模块化的方式(无论是commonJS还是ES6语法)进行开发时,如果直接在HTML文件中引入Js文件,浏览器并不会识别其中的模块化代码。
如果不使用模块化代码,在HTML代码中一个个引用js文件又非常的麻烦。
webpack就是用于模块化打包的工具,他将多个Js文件打包到一个js文件中,在HTML中我们只需要引入webpcak打包好的Js文件,浏览器就可以对模块化代码进行识别了。
2.2 一个webpack简单示例
- 涉及到的文件目录:
-dist //用于存放webpack打包后的文件
--bundle.js //通过webpack打包生成的文件
-src //代码源文件
--compute.js //定义了简单加法和乘法函数,通过commomJs模块化
--info.js //定义了简单的参数,通过ES6模块化
--main.js //项目的入口文件
-index.html //浏览器打开的首页
- src中3个js的代码:
//compute.js
function add(num1,num2){
return num1 + num2
}
function mul(num1,num2){
return num1 * num2
}
module.exports = {
add,
mul
}
//info.js
export const name='Taylor';
export const age='25';
export const type='people';
//main.js
const {add,mul} = require('./compute.js')
console.log(add(20,30));
console.log(mul(20,30));
import {name,age,type} from "./info";
console.log(name);
console.log(age);
console.log(type);
- 在终端中通过webpack打包:
cd 对应目录
webpack ./src/main.js ./dist/bundle.js
- 在index.html中对打包的js文件进行引用:
<script src="./dist/bundle.js"></script>
- 在浏览器中可以看到,模块化js文件就被正常识别了
三、webpack的配置
3.1 入口和出口
每次利用webpack打包时,都需要写上入口和出口作为参数,为了更方便,可以通过将这两个参数写进配置中,在运行直接读取。
- 项目会用到一些node环境的依赖包,所以我们对项目进行一个初始化,生成一个
package.json
文件,用于告诉这个项目的信息,再使用npm install
安装依赖。
cd 对应的目录
npm init
npm install
- 接着创建一个
webpack.config.js
webpack配置文件,名称默认是固定的,在这个文件里可以配置入口和出口参数。(在第1步里安装了依赖,配置文件中的path才可生效)
const path = require('path')
module.exports = {
// 入口,可以为字符串/数组/对象
entry: './src/main.js',
// 出口,通常为一个对象,包含path(绝对路径)和filename(文件名)
output: {
// __dirname:全局变量,保存当前文件所在路径,通过resolve函数拼接路径
path: path.resolve(__dirname,'dist'),
filename:'bundle.js'
}
}
- 此时,在终端中直接输入
webpack
就可以执行打包了。
3.2 命令映射
打包命令webpack
也可以通过配置修改,执行npm run xxx
的命令时,会在package.json
文件scripts
参数下寻找对应的xxx参数进行执行。
- 在package.json文件-
script
中添加build
映射:
{
"name": "learnwebpack",
"version": "1.0.0",
"description": "",
"main": "index.html",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
"author": "",
"license": "ISC"
}
- 添加后,就可以直接在终端中输入以下命令进行打包:
npm run build
3.3 本地webpack
在开发中会遇到项目本身需要的webpack版本和当前全局的webpack版本不同的情况,所以需要在本地进行安装开发时依赖webpack,与当前项目进行同步。
终端中运行(此时使用的本地版本号为3.6.0):
cd 对应目录
npm install webpack@3.6.0 --save-dev
安装好后,可以看到package.json里也多了一个属性:
注:
- 通常一个项目,都有自己的局部webpack。
- 直接在终端中使用webpack,是使用的全局的webpack;使用局部的webpack,需要进入node_modules/.bin/webpack中。
- package.json中的scripts的脚本在执行时,会先找本地,如果没有找到,会去全局的环境变量中寻找。
四、loader
在开发中常常会需要加载css、图片,将ES6转换成ES5,将TypeScript转换成ES5代码,将scss、less转成css,将.jsx、.vue文件转换成js文件的需求等,对于webpack本身来说是不支持转化的,所以需要给webpack扩展对应的loader来支持。
1.通过npm安装需要使用的loader
2.在webpack.config.js中的modules关键字下进行配置
在webpack官方网站上可以找到loader的相关信息:
https://webpack.docschina.org/loaders/
4.1 css文件加载
场景:
在安装了本地webpack的前提下,在项目中新增一个css文件夹,并放置一个bgcolor.css文件,其中含有改变背影颜色的简单样式。
- 根据官方文档上的loader教程,需要先安装
style-loader
和css-loader
(loader的版本和webpack的版本可能会互相影响,需要下载当前webpack版本支撑的loader,直接@版本号即可)。在终端执行以下命令:
npm install --save-dev style-loader
npm install --save-dev css-loader@2.0.2
- 然后在
webpack.config.js
文件中添加配置:
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
],
},
};
注:
- css-loader负责将css文件进行加载;
- style-loader负责将样式添加到DOM中;
- use中使用多个loader,是从右向左的顺序加载
4.2 less文件加载
旧版的less引入和现在官网的新版有细微区别,还是在css中添加了一个less文件,然后在mian中引入。
- 当前webpack本地版本为3.6.0,下载对应版本的less-loader:
npm install --save-dev less-loader@4.1.0 less@3.9.0
- webpack.config.js文件中的配置:
{
test: /\.less$/,
use:[{
loader: "style-loader"
},{
loader: "css-loader"
},{
loader: "less-loader"
}]
}
4.3 图片加载
webpack有两种可以用来加载图片的loader,分别是url-loader
和file-loader
。
4.3.1 url-loader
场景:
在src
目录下新建一个img
目录并放入一张名为time.png
的小于8kb的图片,然后在css
目录下新建一个style.css
文件引入那张图片,再在main.js
中导入css文件:
body{
background-image: url('../img/time.png');
}
- 配置好css-loader、style-loader,本文使用的版本为:
webpack:"^3.6.0",
css-loader: "^2.0.2",
style-loader: "^1.2.1"
- 安装url-loader
npm install url-loader --save-dev
在webpack.config.js
文件中进行配置:
module: {
rules: [
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
},
},
],
}
}
- 配置好后,用webpcak打包,在浏览器中可以看到图片使用base64进行了编码:
4.3.2 file-loader
当图片大于8kb时,可以通过file-loader
将文件解析为url进行显示。
- 首先还是下载file-loader:
npm install file-loader --save-dev
在webpack.config.js中进行配置:
module: {
rules: [
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
name:'img/[name].[hash:8].[ext]',
esModule: false
}
},
],
}
}
在use中除了loader还可以配置option
属性,有如下主要选项:
- img:文件要打包到的文件夹
- name:获取图片的原名称
- hash:8 :保留8位hash
- ext:使用图片原来的扩展名
- 配置好后进行打包会在dist文件输出以hash命名的图片文件(防止重名)。但是默认情况下,webpack会将生成的路径直接返回给使用者,这样会在浏览器中无法显示:
整个程序是打包在dist文件下的,所以我们需要在路径下再添加一个./dist
,直接在webpack.config.js
文件中进行配置:
output: {
path: path.resolve(__dirname,'dist'),
filename:'bundle.js',
publicPath:'./dist/'
}
此时就可以正常显示图片了:
- 可能存在的坑:如果在浏览器中查看到的路径为Object,并非url时,可能是由于file-loader版本的问题。在V4.3.0版本中,file-loader使用了esModule语法,造成了引用图片文件的方式变了,默认情况下会生成使用ES模块语法的JS模块。有以下两种解决方式:
- 引用图片加后缀
<img src="require('img/time2.jpg').default"/>
- 在file-loader配置项里,将option中的esModule配置为false
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
esModule: false
}
},
]
}
五、使用Vue
5.1 引入Vue的配置
- 首先通过npm下载Vue:
npm install --save vue
vue是在开发和运行时都需要的依赖,所以不用加-dev。
- 在
main.js
文件中导入vue并创建Vue实例(挂载index.html中的div):
import Vue from 'vue'
- 在vue依赖包中,有如下两个版本:
-
runtime-only:代码中不可以有任何的template;
-
runtime-compiler:代码中可以有template,compiler可以用于编译template。
此时进行打包,默认使用的是only,而当前在index.html
中挂载的div为实例vue的template,直接在浏览器运行则会报错。所以需要把当前版本改为compiler,在webpack.config.js
中添加resolve
:
module.exports = {
entry: './src/main.js',
output: {...},
module: {...},
resolve:{
alias:{
'vue$':'vue/dist/vue.esm.js'
}
}
}
- 再次打包,vue就可以正常载入了。
5.2 el和template
在实际开发中,并不会直接修改index.html
中的内容,index中往往只留有一个用于挂载的div,而实际显示的代码可以直接写在在Vue实例的template
属性中。
//mian.js
import Vue from 'vue'
new new Vue({
el:'#app',
template:`
<div>
<h2>{{message}}</h2>
<button @click=btnclk()>按钮</button>
</div>
`,
data:{
message:'Hello webpack'
},
methods:{
btnclk(){
console.log('click');
}
}
})
此时,尽管el所挂载的DOM中有代码,也会被template中的内容替代。
5.3 .vue文件
直接将代码写在Vue实例中会使代码看起来太多,在实际开发中常常将渲染代码以组件的形式单独放在一个.vue
文件中,然后在实例中通过组件引用。
.vue
文件:由、
<!-- template标签中放置html代码 -->
<template>
<div>
<h2>{{message}}</h2>
<button @click="btnclk()">一个按钮</button>
<h3 class="title">你好,{{name}}</h3>
</div>
</template>
<script>
export default {
// 给出组件的名字
name:"App",
// 定义动作
data () {
return {
message:"Hello wht",
name:"wht"
};
},
methods: {
btnclk(){
console.log('click!')
}
}
}
</script>
<style scoped>
/* 定义样式 */
.title {
color: blue;
}
</style>
- 在
main.js
中引用vue文件,并创建vue实例,注册组件(名称为.vue文件中定义的name)
import Vue from 'vue'
import App from './vue/App.vue'
new new Vue({
el:'#app',
template:'<App/>',
components:{
App
}
})
- 在下载了vue的前提下,下载支持vue运行的loader
cnpm install vue-loader vue-template-compiler --save-dev
注:vue-loader需要与vue的版本号相同,否则会报版本不匹配的错误,此时可尝试使用@指定版本号,或卸载重新安装的方式。
- 在
webpack.config,js
中进行配置,添加plugins
以及module
:
const path = require('path')
const { VueLoaderPlugin } = require("vue-loader");
module.exports = {
entry: './src/main.js',
output: {...},
plugins: [
new VueLoaderPlugin()
],
module: {
rules: [
{
test: /\.vue$/i,
use: ['vue-loader'],
}
],
}
- webpack成功打包。
六、plugins
webpack中可以添加很多插件。
6.1 版权plugin
const webpack = require('webpack')
module exports = {
...
plugins:[
new webpack.BannerPlugin('xxx版权所有')
]
}
添加的内容就会在bundle.js
的首行显示。
6.2 html打包plugin
发布项目时,发布的是dist文件夹中的内容,所以需要把index.html文件打包到dist文件夹中。
- 下载依赖
//webpack@3.6.0
npm install html-webpack-plugin@3.2.0 --save-dev
- 在
webpack.config.js
中配置,此时需要在html中添加挂载vue实例的div,所以添加一个在根目录下的index.html
为模板:
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
...
plugins: [
...
new HtmlWebpackPlugin({
template:'index.html'
})
],
根目录下的index.html文件只需要包含一个id被挂载的div标签,引入bundle.js文件的script不再需要,打包后会自动生成。
- webpack打包,打包好会在dist文件夹中出现一个index.html文件,其中会自动使用script标签插入bundle.js。
6.3 js压缩plugin
打包后的文件可以通过压缩plugin减少空间占用率,这里为了和CLI2保持一致,使用1.1.1版本的压缩插件。
- 下载依赖:
npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
- 在
webpack.config.js
中配置:
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
...
plugins: [
...
new UglifyjsWebpackPlugin()
],
七、搭建本地服务器
7.1 dev-server
webpack提供了一个基于node.js的本地开发服务器,内部使用的是express框架,可以实现实时刷新的效果,用于服务于某一个文件夹,将内容读取到内存中,使得在项目开发中只用打包一次文件即可。
- 下载依赖:
//webpack@3.6.0
npm install webpack-dev-server@2.9.1 --save-dev
- 在
webpack.config.js
中配置:
module.exports = {
...
devServer:{
contentBase:'./dist',
inline: true,
host:'0.0.0.0'
}
}
- contentBase:服务的文件夹
- inline:是否实时刷新
- host:使得服务器可以通过本机ip访问
- 由于webpack-dev-server是安装在局部的,所以通过以下命令启动服务:
.\node_modules\.bin\webpack-dev-server
此时访问本机ip:8080端口就可以访问到index.html了,修改文件也会实时更新,如果需要退出,执行ctrl+c
。
- 在
package.json
中同样可以像build一样,在script
中给启动服务器做一个快捷键,并且加上open参数,使得执行npm run dev
的时候会自动打开页面(需要去掉devServer中的host参数)
"dev":"webpack-dev-server --open"
7.2 配置分离
在webpcak中有些配置是开发时需要的(devServer),但是发布的时候并不需要,有些是开发时不需要(Uglifyjs),但是发布的时候需要的,所以就需要对配置进行分离,开发和发布分开配置。
- 主要是对
webpack.config.js
进行分离,这里在根目录下创建了一个build文件夹,并添加了3个js文件:
base.config.js //放置通用的配置
dev.config.js //放置开发时需要的配置
prod.config.js //放置发布时需要的配置
然后将webpack.config.js
中的内容复制到base.config.js
后就可以将webpack.config.js
文件删除了,在base.config.js
中将开发时需要的配置语句(如devserver)转移到dev.config.js
中,将发布时需要的配置语句(如uglifyjs)转移到prod.config.js
中。
- dev和prod文件中,需要将配置进行合并,此时需要下载合并的依赖:
npm install webpack-merge --save-dev
在dev.config.js
中配置如下:
const webpackMerge = require('webpack-merge')
const baseConfig = require('./base.config')
module.exports = webpackMerge(baseConfig,{
devServer:{
contentBase:'./dist',
inline: true,
// host:'0.0.0.0'
}
})
在prod.config.js`中配置如下:
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
const webpackMerge = require('webpack-merge')
const baseConfig = require('./base.config')
module.exports = webpackMerge(baseConfig,{
plugins: [
new UglifyjsWebpackPlugin()
]
})
- 此时配置就已经分离好,接着需要修改
package.json
中的script用于修改打包时执行的配置文件:
"build": "webpack --config ./build/prod.config.js",
"dev": "webpack-dev-server --open --config ./build/dev.config.js"
- 由于
base.config.js
文件是放置于根目录下的config文件夹中的,所以出口文件也应该对应修改:
...
output: {
path: path.resolve(__dirname,'../dist')
...
}
- 分离完成,执行打包或devserver,正常运行。