webpack
什么是webpack?
-
官方解释:
- At its core,webpack is a static moudle bundler for modern JavaScript applications
- 从本质上来讲,webpack 是一个现代化的JavaScript应用的静态模块打包工具。
-
但是他到底是什么呢?(两个关键字)
- 模块
- 打包
-
前段模块化:
- 在前面学习中,我们已经用了大量的篇幅解释为什么前段需要模块化
- 而且我也提到了目前使用前段模块化的一些方案:AMD,CMD,CommonJS,ES6
- 在ES6之前,我们要想进行模块发开发,就必须借助于其他工具,让我们可以进行模块化开发
- 并且在通过某快画开发完成了项目后,还需要处理模块间的各种依赖,并且将其进行整合打包
- 而 webpack其中一个核心就是让我们可能进行模块化开发,并且会帮助我们处理模块间的依赖关系
- 而且不仅仅是JS文件,我们的CSS,图片,json文件等等webpack中都可以被当做某块来使用
- 这就是webpack模块化的概念。
-
如何理解打包的概念
- 就是webpack中的各种资源模块进行打包合并成一个或多个(Bundle)
- 并且在打包的过程中,还可以对资源进行处理,比如压缩图片,将scss转成css,将ES6语法转成ES5语法,将TypeScript 转成JavaScript等等操作
- 但是打包的操作似乎grunt、gulp 也可以帮助到我们,那么有什么不同呢
-
和grunt/gulp的对比
- grunt/gulp 的核心是Task
- 我们可以配置一系列的task,并且定义task要处理的事务(例如ES6,ts转化,图片压缩,scss转成css)
- 之后让grunt/gulp 来一次执行这些task,而且让整个流程自动化
- 所以grunt/gulp也被成为掐断自动化管理工具
-
什么时候使用grunt/gulp?
- 如果你的工程模块非常简单,甚至是没有用到模块化的概念
- 只需要进行简单的合并、压缩,就使用grunt/gulp即可
- 但是如果整个项目使用了模块化管理,而且相互依赖非常强,则选择webpack更优
-
所以,grunt/gulp 和webpack 有什么不同呢?
- grunt/gulp 更强调的是前段流程的自动化,模块不是他的核心
- webpack 更加强调模块化开发管理,而文件压缩合并,预处理等功能,是他附带的功能。
webpack安装
- webpack是模块化的打包工具
- 我们使用模块化开发,无法给用户直接使用,需要通过webpack打包。
- webpack为了可以正常运行,必须依赖node环境。
- node环境为了可以正常执行很多代码,必须依赖其中很多的包
- npm 就是管理这些node依赖包的工具(node packages manager)
- 所以安装webpack之前首先需要安装Node.js ,Node.js 自带了软件包管理工具npm
- 查看node版本:node -v
- 使用npm安装webpack:
# 安装webpack
# @ 后面加版本号
# -g 指的是全局安装
npm install webpack@3.6.0 -g
- 简单的打包命令
# 利用webpack将 main.js 打包成 dist文件夹下的bundle.js
# webpack 会帮你处理各个模块间的依赖
webpack ./src/main.js ./dist/bundle.js
我们正常一个vue项目会有 一个src文件和一个dist文件夹
- src文件夹:用于存放项目源码
- dist文件夹:用于存放打包后的文件(distribution(发布))
本地安装(局部安装)webpack
# --save-dev 开发时依赖 webpack一旦打包好就不需要了,所以属于开发时的依赖
npm install webpack@3.6.0 --save-dev
package.json 中定义启动
-
但是每次执行都需要敲一长串的命令,并不是很方便
- 我们其实可以在package.json 的script 中定义自己的执行脚本
-
Package,json 中的script 的脚本在示性式,会按照一定顺序寻找对应的命令
- 首先,会虚招本地的node_modules/.bin 路径中对应的命令
- 如果没找到,会去全局的环境变量中寻找
-
如何执行我们的buld 指令:
npm run build
Package.json 配置信息
{
"name": "meetwebpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^3.6.0"
}
}
什么是loader?
上链接:https://www.webpackjs.com/loaders/
- loader 是webpack非常核心的一个概念
- webpack用来做什么呢?
- 在我们之前的实例中,我们主要是用webpack来处理我们写的js代码,并且wbpack会自动处理js之间的依赖
- 但是,在开发中我们不仅仅有几本的极速代码处理,我们也需要加载css,图片,也包括一些高级的将ES6转ES5的代码,将TypeScript 转成ES5 代码,将scss ,less 转成css, 将.jsx .vue 转成js文件等
- 对于webpack本身的能力来说这是不支持的
- 但是,webpack 扩展对应的loader 就可以了
- loader的使用过程:
- 1,通过npm安装需要使用的loader
- 2,在webpack.config.js中的moudules关键字下面进行配置
- 大部分loader我们都可以在webpack官网中找到,并且学习对应的方法
less文件处理-准备工作
- 如果我们希望在项目中使用less,scss,stylus来写样式,webpack是否可以帮助我们处理呢?
- 我们以less为例,其他都一样。
- 我们还是先创建一个less文件,依然放在css文件夹中
ES6语法处理
- 如果仔细阅读webpack中打包的js文件,发现写的ES6语法并没有转成ES5,name就以为这可能一些对ES6还不支持的浏览器没有办法很好的运行我们的代码
- 在前面我们说过,如果希望将ES6的语法转成ES5的,那么久需要babel
- 上链接:https://www.webpackjs.com/loaders/babel-loader/
- 而在webpack中,我们直接使用babel对应的loader久可以了
npm install --save-dev babel-loader babel-core babel-preset-es2015
- 配置webpack.config.js 文件
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: ‘babel-loader‘,
options: {
presets: [‘es2015‘]
}
}
}
]
}
// 或者你可以使用options属性来给loader传递参数
module: {
rules: [
{
test: /\.js$/,
// exclude 排除 下面文件夹中的文件不需要传唤
exclude: /(node_modules|bower_components)/,
use: {
loader: ‘babel-loader‘,
options: {
presets: [‘@babel/preset-env‘],
plugins: [require(‘@babel/plugin-transform-object-rest-spread‘)]
}
}
}
]
}
vue发布的版本问题:
- runtime-only —> 代码中,不可以有任何template
- runtime-complier —> 代码中,可以有template ,因为有 complier 可以用于编译template
注意:通过 npm install vue —save 直接安装vue 后,在 项目中使用vue ,然后通过webpack打包后运行会报错。
- 修改完成后,重新打包,运行程序:
- 打包过程中没有任何问题(因为只是多打包了一个vue的js文件而已)
- 但是运行程序,没有出现你想要的下过,而且浏览器中报错
- 这个错误说的是我们使用的是runtime-only 版本的vue。
[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.
(found in <Root>)
解决办法:
上链接:
// package.json
{
"name": "meetwebpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^3.6.0"
},
"dependencies": {
"vue": "^2.6.11"
}
}
// wepack.config.js
// 这个文件是配置webpack的入口和出口文件
// node.js 语法获取绝对路径
// 1. 导入path模块,聪node包中找的
const path = require(‘path‘);
module.exports = {
entry:‘./src/main.js‘,
output:{
path:path.resolve(__dirname,‘dist‘), // 必须是绝对路径,这里是通过node.js 的path 实现的路径的拼接
filename:‘bundle.js‘ // bundle 打包
},
// vue 配置这个可以使用template
resolve:{
// alias: 别名
alias:{
// 当我们 导入 from vue 的时候,指向执行的路径去找文件 vue.esm.js 中包含 complier
‘vue$‘:‘vue/dist/vue.esm.js‘
}
}
};
认识plugin(插件)
- 什么是plugin?
- plugin是插件的意思,通常是用于对某个现有的架构进行扩展
- webpack中的插件,就是对webpack现有功能的各种扩展,比如打包优化,文件压缩等
- loader 和 plugin区别
- loader 朱勇用于转换某些类型的模块,它是一个转换器
- plugin是插件,它是对webpack本身的扩展,是一个扩展器
- plugin的使用过程:
- 步骤一:通过npm安装需要使用的plugins(某些webpack已经内置的插件不需要安装)
- 步骤二:在webpack.config.js中的plugins中配置插件
- 下面我们通过示例来看看可以通过哪些插件对现有的webpack打包过程进行扩容,让我们webpack变得更加好用
插件一(版权申明插件)
作用:在bundle.js 中 增加版权信息,即为打包的文件添加版权申明
- 该插件名称叫做 BannerPlugin,属于webpack自带的插件
- 按照下面的方式来修改webpack.config.js文件:
const path = require(‘path‘)
const webpack = require(‘webpack‘)
module.exports = {
....
plugins:[
new webpack.BannerPlugin(‘版权相关申明信息‘)
]
}
插件二(打包html的plugin)
- 目前,我们的index.html文件是存放在项目更睦邻居下面的
- 我们知道,在真是发布项目时,发布的是dist文件夹中的内容,但是dist文件夹中如果没有index.html 文件,name打包的js等文件也就没有意义了。
- 所以,我们需要将index.html 文件打包到dist文件夹中,这个时候就可以使用HtmlWebpackPlugin插件
- HtmlWebpackPlugin插件可以为我们做这些事:
- 自动生成一个index.html 文件(可以执行模板来生成)
- 将打包的js文件,自动通过script标签插入到body中
- 安装HtmlWebpackPlugin插件
npm install html-webpack-plugin@3.2.0 --save-dev
# 注:这里如果在使用的过程中报错,则看 一下 package.json 文件中的版本号,我这里改成3.2.0 就不报错了
- 使用插件,修改webpack.config.js 文件中的plugins部分的内容如下:
- 这里的template表示根据什么某班来生成index.html
- 另外,我们需要删除之前的output中添加的publicPath属性
- 否则插入的script 标签中的src可能会有问题
配置文件如下:(webpack.config.js)
// 这个文件是配置webpack的入口和出口文件
// node.js 语法获取绝对路径
// 1. 导入path模块,从node包中找的
const path = require(‘path‘);
const webpack = require(‘webpack‘);
const HtmlWebpackPlugin = require(‘html-webpack-plugin‘);
module.exports = {
entry:‘./src/main.js‘,
output:{
path:path.resolve(__dirname,‘dist‘), // 必须是绝对路径,这里是通过node.js 的path 实现的路径的拼接
filename:‘bundle.js‘ // bundle 打包
},
module:{
rules:[
{
test: /\.css$/,
// css-loader 只负责将css文件进行加载
// style-loader 负责将样式添加到dom中进行操作
// webpack 在使用多个loader的时候,是从右向左读的
use: [ ‘style-loader‘, ‘css-loader‘ ]
},
{
test:/\.vue$/,
use:[‘vue-loader‘]
},
]
},
resolve:{
// alias: 别名
alias:{
// // 当我们 导入 from vue 的时候,指向执行的路径去找文件 vue.esm.js 中包含 complier
// 该配置项可以让js以 不是runtime-complier 发行版运行,此时可以用template
‘vue$‘:‘vue/dist/vue.esm.js‘,
// // 配置完如下配置可以在导入的时候,可以省略后缀名
// extensions:[‘.js‘,‘.vue‘,‘.css‘],
}
},
plugins:[
new webpack.BannerPlugin(‘最终版权信息归qzk所有‘),
// ########### 生成html #############
new HtmlWebpackPlugin(
{
// 指定模板
template: ‘index.html‘,
}
),
]
};
插件三(js压缩的Plugin)
- 在项目发布之前,我们必然需要对js等文件进行压缩处理
- 这里我们用过插件对文件进行压缩处理
- 我们使用第三方的插件 uglifyjs-webpack-plugin, 并且指定版本号1.1.1 和CLI12 保持一致
npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
- 修改webpack.config.js 文件,使用插件:
const path = require(‘path‘);
const webpack = require(‘webpack‘);
const uglifyJsPlugin = require(‘uglifyjs-webpack-plugin‘);
module.exports = {
...
plugins:[
new webpack.BannerPlugin(‘版权申明‘),
new uglifyJsPlugin()
]
}
执行重新 npm run build 后 查看打包后就是被重新压缩过
搭建本地服务器
- webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js 搭建,内部使用express 框架,可以实现我们想要的让浏览器自动刷新显示我们修改后的结果
- 不过他是单独的某块,在webpack中使用之前需要先安装它
npm install --save-dev webpack-dev-server@2.9.1
- Devserver 也是作为webpack中的一个选项,选项本身可以设置如下属性:
- contentBase:为哪一个文件夹提供本地服务,某人是根文件夹,我们这里要填写 ./dist
- port : 端口号
- inline:页面实时刷新
- historyApiFallback: 在SPA页面中,一栏HTML5的history模式
- webpack.config.js 文件配置修改如下:
{
"name": "meetwebpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config ./build/prod.config.js",
"dev": "webpack-dev-server --open --config ./build/dev.config.js"
},
"author": "qzk",
"license": "ISC",
"devDependencies": {
"css-loader": "^3.5.3",
"html-webpack-plugin": "^3.2.0",
"style-loader": "^1.2.1",
"uglifyjs-webpack-plugin": "^1.1.1",
"vue-loader": "^13.7.3",
"vue-template-compiler": "^2.6.11",
"webpack": "^3.6.0",
"webpack-dev-server": "^2.9.1",
"webpack-merge": "^4.2.2"
},
"dependencies": {
"vue": "^2.6.11"
}
}
- 我们可以再配置另外一个scripts:
- —open 参数就表示直接打开浏览器
- 此时配置完成后启动项目就应该使用 如下命令:
# 相对路径(不是最终的方案)
./node_modules/.bin/webpack-dev-server
// 这个文件是配置webpack的入口和出口文件
// node.js 语法获取绝对路径
// 1. 导入path模块,从node包中找的
const path = require(‘path‘);
const webpack = require(‘webpack‘);
const HtmlWebpackPlugin = require(‘html-webpack-plugin‘);
const uglifyJsPlugin = require(‘uglifyjs-webpack-plugin‘);
module.exports = {
...
devServer:{
// 这个server服务于哪一个文件夹
contentBase:‘./dist‘,
// 实时监听变化,更新
inline:true,
// 指定端口
port:8008
}
};
最后就可以通过如下命令来执行了
npm run dev
webpack中配置文件的分离
npm install webpack-merge --save-dev
在实际开发与生产中,我们所依赖的配置文件,放在与src同级的build文件夹下一般分为:
- base.config.js ——生产环境和开发环境都依赖的一些配置文件
- prod.config.js ——生产环境需要,但是开发环境不需要的配置
- dev.config.js ——开发环境需要的,生产环境不需要的配置文件
base.config.js
// 这个文件是配置webpack的入口和出口文件
// node.js 语法获取绝对路径
// 1. 导入path模块,从node包中找的
const path = require(‘path‘);
const webpack = require(‘webpack‘);
const HtmlWebpackPlugin = require(‘html-webpack-plugin‘);
module.exports = {
entry:‘./src/main.js‘,
output:{
path:path.resolve(__dirname,‘../dist‘), // 必须是绝对路径,这里是通过node.js 的path 实现的路径的拼接
filename:‘bundle.js‘ // bundle 打包
},
module:{
rules:[
{
test: /\.css$/,
// css-loader 只负责将css文件进行加载
// style-loader 负责将样式添加到dom中进行操作
// webpack 在使用多个loader的时候,是从右向左读的
use: [ ‘style-loader‘, ‘css-loader‘ ]
},
{
test:/\.vue$/,
use:[‘vue-loader‘]
},
]
},
resolve:{
// alias: 别名
alias:{
// // 当我们 导入 from vue 的时候,指向执行的路径去找文件 vue.esm.js 中包含 complier
// 该配置项可以让js以 不是runtime-complier 发行版运行,此时可以用template
‘vue$‘:‘vue/dist/vue.esm.js‘,
// // 配置完如下配置可以在导入的时候,可以省略后缀名
// extensions:[‘.js‘,‘.vue‘,‘.css‘],
}
},
plugins:[
new webpack.BannerPlugin(‘最终版权信息归qzk所有‘),
new HtmlWebpackPlugin(
{
// 指定模板
template: ‘index.html‘,
}
),
],
};
prod.config.js
// node.js 语法获取绝对路径
// 1. 导入path模块,从node包中找的
const uglifyJsPlugin = require(‘uglifyjs-webpack-plugin‘);
const webpackMerge = require(‘webpack-merge‘);
const baseConfig = require(‘./base.config‘);
module.exports = webpackMerge(baseConfig,{
plugins:[
new uglifyJsPlugin(),
],
});
dev.config.js
const webpackMerge = require(‘webpack-merge‘);
const baseConfig = require(‘./base.config‘);
module.exports = webpackMerge(baseConfig,{
// 开发时依赖,但是生产时不依赖,所以不能放在base里面
devServer:{
// 这个server服务于哪一个文件夹
contentBase:‘./dist‘,
// 实时监听变化,更新
inline:true,
// 指定端口
port:8008
}
});
然后在package.json 中配置 script
{
"name": "meetwebpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
# 配置生产时的依赖的 webpack.config.js
"build": "webpack --config ./build/prod.config.js",
# 配置 开发时的依赖的 webpack.config.js
"dev": "webpack-dev-server --open --config ./build/dev.config.js"
},
"author": "qzk",
"license": "ISC",
"devDependencies": {
"css-loader": "^3.5.3",
"html-webpack-plugin": "^3.2.0",
"style-loader": "^1.2.1",
"uglifyjs-webpack-plugin": "^1.1.1",
"vue-loader": "^13.7.3",
"vue-template-compiler": "^2.6.11",
"webpack": "^3.6.0",
"webpack-dev-server": "^2.9.1",
"webpack-merge": "^4.2.2"
},
"dependencies": {
"vue": "^2.6.11"
}
}