1.什么是webpack?
从本质上来讲,webpack是一个现代的Javascript应用的静态模块化打包工具。
这里的关键点就是模块和打包
1.1前端模块化
(1)在这之前大家已经知道了在前端开发中为什么要进行模块化开发,目前使用的前端模块化方案有:AMD、CMD、CommonJs、ES6。
(2)在ES6之前我们要想进行模块化开发,必须借助于其他的工具,让我们可以进行模块化开发。并且在模块化开发完成后,我们还需要处理各种模块之间的依赖关系,并且将他们进行整合打包。
(3)而webpack其中一个核心就是让我们可以进行模块化开发,并且会帮助我们处理模块之间的依赖关系。
(4)在这里不仅仅是Javascript文件,我们的css、图片、json文件等再webpack中都可以被当做模块来使用,这就是webpack模块化的概念。
1.2打包
(1)打包这个概念比较好理解了,就是利用webpack将各种资源进行打包合并为一个或者多个包(bundle)。在打包过程中,还可以对资源进行处理,比如压图片,将scss转成css,将ES6语法转成ES5语法,将TypeScript转成Javascript等等操作。
(2)在这里我们会想到,gulp也可以进行资源的打包,那么它和webpack有什么区别呢?
*webpack和grunt/gulp对比
(1)grunt/gulp的核心是Task
我们可以配置一系列的task,并且定义task需要处理的事务(例如ES6转ES5,ts转化,图片压缩,scss转成css),之后让grunt/gulp来执行这些task,而且让整个流程自动化,所以grunt/gulp也被称为前端自动化任务管理工具
(2)什么时候用grunt/gulp呢?
如果你的项目模块依赖非常简单,或者是没有用到模块化的概念,只需要进行简单的合并和压缩,就使用grunt/gulp即可。但是如果整个项目使用了模块化管理,而且相互之间依赖非常强,我们就可以使用更加强大的webpack了。
(3)所以webpack和grunt/gulp有什么不同呢?
* grunt/gulp更强调的是前端流程的自动化,模块化不是它的核心。
* webpack更加强调模块化开发管理,而文件压缩合并,预处理等功能,是他附带的功能。
2.webpack的安装
* webpack为了可以正常运行必须依赖node环境。node自带了包管理工具(npm)。
执行指令:npm install [email protected] -g 进行全局安装。这里指定了下载3.6.0的版本。
(1)为什么全局安装后还需要局部安装呢?
在终端直接执行webpack命令,使用的是全局安装的webpack,但是当我们在package.json中定义了script时,其中包含了webpack命令,那么使用的是局部的webpack。
3.webpack的基本使用
(1)在webpack打包中一般目录结构:
src(源码):用于存放我们的源文件
main.js: 项目的入口文件(这里有时候也可以命名为index.js)。
aaa.js定义了一些函数,可以在其他地方引用,并且使用。
dist(distribution发布):用于存放之后打包的文件。
index.html:浏览器打开展示的首页html。
package.json:通过npm init 生成的,npm包管理的文件。
(2)在src文件下我们写的东西都可通过模块化的方式去实现(AMD/CMD/CommonJs/ES6都可以)。
用CommonJs模块化进行模块化开发
CommonJs方式导出:module.exports = {}
在入口文件main.js导入:const {add , mul} = require('./aaa.js');
用ES6的模块化进行模块开发
ES6方式导出:export const name = '库里';
export const age = 18;
在入口文件main.js中导入: import {name, age} from './info.js'
这个时候只需要用webpack打包一下,它会自动的帮我们处理模块之间相互之间的依赖,处理完成后它会生成一个最终的js文件(bundle.js),我们在引用的时候只需要用最终生成的那个js文件就可以了。
4.webpack.config.js和package.json的配置
(1)配置webpack.config.js文件:
const path = require('path'); // 这里需要用到node里面的path模块,这里用到的是node里面的相关的包(执行npm init,生成一个package.json文件,这个文件是告诉我们项目中的一些相关信息的)。
* 当package.json中依赖相关东西的话,我们需要执行npm install,会根据里面所有的依赖帮助我们安装一些所需要的东西。
module.exports= {
entry: ' ./src/main.js ', // 入口文件
output: { // 出口文件
path: path.resolve(_dirname, 'dist'), // 这里要动态获取一个绝对路径,这里引用到了一个path模块中的resolve函数,用来对我们两个路径拼接(_dirname这里对这个上下文变量进行拼接)。
filename: ' bundle.js '
}
}
当我们在webpack.config.js中进行了以上配置后,我们就可以在终端直接执行webpack命令实现对项目的打包。
(2)如何用npm run build 命令替代webpack命令执行打包效果?
这里还需要把二者进行一个映射:
打开package.json,对文件中scripts进行配置
"scripts": {
"build": webpack
}
* 开发时依赖(这就是在本地安装webpack要加--save-dev的原因,因为webpack只有在开发时需要,打包上传到服务器后就没用了)
*运行时依赖
前面有说到除了全局安装一个webpack之外,我们还要在本地安装一个webpack。执行npm install [email protected] --save-dev。
* 在终端中敲命令时,用到的webpack都是全局的,但是当我们在package.json中定义scripts脚本时,然后执行npm run build,此时用到的就是本地的webpack。
5.什么是loader?
laoder在webpack中是一个非常核心的概念。在开发中我们不仅仅有基本的js代码处理,也需要加载css、图片ES6的转化,以及将.vue文件转成js文件等等,对于webpack本身的能力来说,对于这些转化是不支持的,这时候就需要用到loader解决。
loader使用过程:
(1)通过npm安装需要使用的loader
(2)在webpack.config.js中的modules关键字下面进行配置
5.1webpack中css文件的配置
安装style-loader: npm install style-loader --save-dev
* 注意:style-loader要放在css-loader前面,为什么呢?因为webpack在读取使用loader的过程中,是按照从右向左顺序读取的。负责将样式添加到DOM中
安装css-loader: npm install css-loader --save-dev
* 这里如果只引入css-loader会发现样式没有生效,这是因为css-loader只负责加载css文件,但是并不负责将css具体样式嵌入到文档中,这个时候我们还需要引入一个style-laoder。
在webpack.config.js中进行配置(具体参考wabpack的官方文档:https://www.webpackjs.com/)
module: {
rules: [
{
test: /\.css$/,
use: [ ' style-loader ' , ' css-loader ' ]
}
]
}
5.2less文件处理
安装less-loader: npm install less-loader less --save-dev
* 通过less加载对应的less文件,下载同时还会下载一个less的包,可以对less文件的相关代码进行一个解析。
webpack.config.js中配置如下:
module: {
rules: [
{
test: /\.css$/,
use: [ ' style-loader ' , ' css-loader ' , 'less-loader'] // 这里也可以用对象的形式比如:loader:'style-loader'
}
]
}
5.3webpack图片文件的处理
安装url-loader: npm install --save-dev url-loader
* 当我们需要加载比limit大的图片时,我们需要下载file-loader。
安装file-loader: npm install --save-dev file-loader
webpack.config.js中配置如下:
module: {
rules: [
{
test: /\.(png|jpg|jpeg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
* 当加载的图片小于limit时,会将图片编译成base64字符串形式。
* 当加载的图片大于limit时,需要使用file-loader模块进行加载。
}
}
]
}
]
}
* 我们发现,在图片打包的时候webpack自动帮助我们生成了一个非常长的名字,这是一个32位的hash值,目的就是防止名字重复,但是实际开发中我们对名字也有一定的要求,比如讲所有的图片放在一个文件夹中,跟上原来图片的名称,同时还要防止重复。
所以我们可以在options中添加上如下选项:
img: 文件要打包到的文件夹
name: 获取图片原来的名字,放在该位置。
hash:8 为了防止图片名称冲突,依然使用hash,但是只保留8位数。
ext: 使用图片原来的扩展名
例子: name: 'img/[name].[hash:8].[ext]';
5.4 ES6转ES5的babel
安装babel-loader: npm install --save-dev babel-loader@7 babel-core babel-preset-es2015
*在webpack中,ES6转ES5需要使用对应的loader,这时候就需要下载babel-loader
webpack.config.js中配置如下:
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/, // 排除
use: [
{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
]
}
]
}
5.5 webpack中vue配置过程
runtime-only // 这种版本代码中不可以有任何的templete
runtime-compiler // 这种版本代码中可以有template,因为有compiler可以用于编译template
过程:
(1)安装vue: npm install --save vue
(2)引入 import Vue from 'vue'
(3)由于vue版本不太对(runtime-only/runtime-complier),通过alias(别名)解决版本错误。
vue对应loader安装: npm install vue-loader vue-template-compiler --save-dev
webpack.config.js中配置如下:
module: {
rules: [
{
test: /\.vue$/,
use: [
{
loader: 'vue-loader',
4 }
]
}
]
}
* package.json中的内容修改后都要执行一下npm install重新做一下安装
5.6 el和template的区别
* 当你定义了一个template属性,此时又有el属性挂载,这时候template定义的东西会替换掉el属性所挂载的所有代码
6. webpack-认识plugin(插件)
* plugin是插件的意思,通常用于对现有的框架进行扩展。
*webpack中的插件,就是对webpack现有的功能进行各种扩展,比如打包优化,文件压缩等等。
*loader和plugin的区别: loader用于转换某些类型的模块它是一个转换器。plugin是插件,它是对webpack本身的扩展,是一个扩展器。
*plugin使用过程:
通过npm安装需要使用的plugin,在webpack.config.js中配置插件。
(1)BannerPlugin的使用,添加版权信息
plugins: [
new wabpack.BannerPlugin('最终版权归我所有')
]
(2)HtmlWebpackPlugin的使用
* 把根目录下的index.html打包到dist文件中
* 这个插件可以自动生成一个index.html文件(可以指定模板来生成),将打包的js文件,自动通过script标签插入到body中
安装:npm install html-webpack-plugin --save-dev
配置webpack.config.js:
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 导入
plugins: [
new wabpack.BannerPlugin('最终版权归我所有'),
new HtmlWebpackPlugin({
template: 'index.html', // 这里是可以指定模板的,指定某一个模板作为dist文件中生成的html文件的模板。
})
]
(3)UglifyjsWebpackPlugin的使用
* 对打包的js文件进行丑化压缩的插件
* 我们使用一个第三方的插件uglifyjs-webpack-plugin,并且版本号指定1.1.1,和cli2保持一致
安装: npm install [email protected] --save-dev
配置webpack.config.js:
const UglifyjsWebpackPlugin = require('html-webpack-plugin'); // 导入
plugins: [
new wabpack.BannerPlugin('最终版权归我所有'),
new HtmlWebpackPlugin(),
new UglifyjsWebpackPlugin(),
]
7.搭建本地服务器
* webpack提供了一个可选的本地开发服务器,这个本地服务器基于nodejs搭建,内部使用express框架,可以实现我们想要的让浏览器自动刷新显示我们修改后的结果。
它是一个单独的模块,在webpack中使用它需要先安装:
npm install --save-dev [email protected]
devserver也是作为webpack中的一个选项,选项本身可以设置以下属性:
contentBase: 为哪一个文件夹提供本地服务,默认是根文件夹,我们这里要填写./dist
port: 端口号
inline: 页面实时刷新
historyApiFallback: 在SPA页面中,依赖HTML5的history模式
配置webpack.config.js:
devServer: {
contentBase: './dist',
inline: true
}
开发时执行脚本可以在package.json中加入'dev': 'weback-dev-server --open',这时候执行npm run dev就可以启动本地服务器,加入--open可以实现执行命令自动打开浏览器。
* 开发阶段不要加入UglifyjsWebpackPlugin插件,在发布阶段再加入,因为丑化了js文件后,调试阶段不好阅读
8.webpack配置文件的分离
* 有的配置是需要打包时候使用,比如说丑化js等等,有的配置是需要本地开发时候使用,比如说配置本地服务器。这里就需要把webpack配置进行一个分离,根据开发时候的配置和发布时候的配置。
这时候需要把以前的webpack.config.js分成三个不同的js,这三个不同的js放在根目录下的build文件夹中分别是:
base.config.js : 里面存放的是一些公共的配置
prod.config.js :生产环境的一些配置
dev.config.js : 开发环境的一些配置
这么分的思路就是我们把开发和生产环境的配置实现一个分离,这时候需要安装一个webpack-merge的模块,它的作用是把base和prod,base和dev,分别合并,这样就实现了开发和生产环境配置的分离。
安装: npm install webpack-merge --save-dev
prod和base合并:
在prod.config.js中:
const baseConfig = require('./base.config.js');
module.exports = { baseConfig,{
plugins: [
new UglifyjsWebpackPlugin(),
]
})
在dev.config.js中:
const baseConfig = require('./base.config.js');
module.exports = { baseConfig,{
devServer: {
contentBase: './dist',
inline: true
}
})
但是这时候你要是执行npm run build是会报错的,因为我们现在已经把webpack.config.js删了,这时候我们就需要在package.json中指定一下用哪个配置文件:
"scripts": {
"build": ‘webpack --config ./build/prod.config.js',
"dev": 'webpack-dev-server --open --config ./build/dev.config.js'
}
这时候就可以正常打包了,但是此时打包完成的dist文件是加到了build文件夹中,这个是因为我们在base.config.js中出口output拼接的目录路径错误,修改一下目录就可以了。