认识

官方文档

简介:本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph)*,其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 *bundle

image-20200921194136709

两个点来解释上面这段话:模块打包

前端模块化

  1. webpack其中一个核心就是让我们可能进行模块化开发,并且会帮助我们处理模块间的依赖关系。

  2. 而且不仅仅是JavaScript文件,我们的CSS、图片、json文件等等在webpack中都可以被当做模块来使用(在后续我们会看到)。

打包

  1. 将webpack中的各种资源模块进行打包合并成一个或多个包(Bundle)。
  2. 并且在打包的过程中,还可以对资源进行处理,比如压缩图片,将scss转成css,将ES6语法转成ES5语法,将TypeScript转成JavaScript等等操作。

和grunt/gulp的对比

  1. grunt/gulp更加强调的是前端流程的自动化,模块化不是它的核心。
  2. webpack更加强调模块化开发管理,而文件压缩合并、预处理等功能,是他附带的功能。

安装

要先安装node

检测node环境

1
node -v

安装

1
npm i webpack --save-dev

为什么全局安装后要还要局部安装

  1. 在终端直接执行webpack命令,使用的全局安装的webpack
  2. 当在package.json中定义了scripts,其中包含了webpack命令,那么使用的是局部webpack

起步

  1. 创建对应的文件夹

    • dist文件夹:用于存放之后打包的文件
    • src文件夹:用于存放我们写的源文件
    • main.js:项目的入口文件。
    • mathUtils.js:定义了一些数学工具函数,可以在其他地方引用,并且使用。
    • index.html:浏览器打开展示的首页html
    • package.json:通过npm init生成的,npm包管理的文件
  2. 打包指令

    1
    webpack src/main.js dist/bundle.js
  3. index.html使用打包后的文件

    1
    <script src="dist/bundle.js"></script>

    配置

入口和出口

创建一个webpack.config.js文件用来将webpack打包命令进行简化,从而直接在配置中定义出口和入口

注意:文件名字必须命名为webpack.config.js

1
2
3
4
5
6
7
8
9
const path = require('path')

module.exports = {
entry: './public/02-inport.js',
output: {
path: path.join(__dirname,'dist'),
filename: 'bundle.js'
}
}

局部安装webpack的问题

启动webpack进行打包需要定位到node_modules/.bin/webpack才能执行webpack命令,或者,通过下面的方式

package.json中定义启动

在package.json中定义启动有什么优势呢

package.json中的scripts的脚本在执行时,会按照一定的顺序寻找命令对应的位置。

首先,会寻找本地的node_modules/.bin路径中对应的命令。

如果没有找到,会去全局的环境变量中寻找

下面介绍一下使用

  1. 首先是在json文件下定义npm命令

    1
    2
    3
    4
    "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "npx webpack"
    },
  2. cmd执行命令

    1
    npm run build

    loader

简介

loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。

本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。

在更高层面,在 webpack 的配置中 loader 有两个目标:

  1. test 属性,用于标识出应该被对应的 loader 进行转换的某个或某些文件。
  2. use 属性,表示进行转换时,应该使用哪个 loader。

loader通用使用过程

  1. 通过npm安装需要使用的loader
  2. 在webpack.config.js中的modules关键字的loaders下进行配置

css-loader、style-loader

作用

转换css文件(css-loader)并对其进行解析(style-loader)后样式改变

安装

1
npm install --save-dev css-loader style-loader

main.js文件导入css模块

1
import css from 'file.css'

webpack.config.js配置

1
2
3
4
5
6
7
8
9
10
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
],
},
};

这里解释一下,为什么style-loader在css-loader前面

因为webpack在读取使用的loader的过程中,是按照从右向左的顺序读取的

less-loader

安装

1
npm install --save-dev less-loader less

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
test: /\.less$/,
use: [{
loader: 'style-loader',
},
{
loader: 'css-loader',
},
{
loader: 'less-loader',
options: {
lessOptions: {
strictMath: true,
},
},
},
],
},

这样就可以解析less文件

图片处理

url-loader

安装

1
npm install --save-dev url-loader

配置

1
2
3
4
5
6
7
8
9
10
{
test: /\.(png|jpg|gif|jpeg)$/i,
use: [{
loader: 'url-loader',
options: {
limit: 11000,
name: 'img/[name].[hash:6].[ext]',
},
}, ],
},

打包后你会发现,运行index.html,在f12窗口中查看一下,你会发现背景图是通过base64显示出来的,这也是limit属性的作用,当图片小于11kb时,会对图片进行base64编码

但是,如果你放了一张超过11kb的照片后,你会发现,打包的时候它会报错,这个时候我们就需要安装另外一个模块file-loader

file-loader

安装

1
npm install --save-dev file-loader

配置

这里要注意,不能同时配置url-loader和file-loader,也就是说你配了url-loader后就不要去配置file-loader,反过来也是,你配置了file-loader后就不要去配置url-loader了,否则打包的时候会报错

1
2
3
4
5
6
7
{
test: /\.(png|jpe?g|gif)$/i,
loader: 'file-loader',
options: {
name: 'img/[name].[hash:6].[ext]',
},
},

修改文件名字

我们发现webpack自动帮助我们生成一个非常长的名字

  • 这是一个32位hash值,目的是防止名字重复
  • 但是,真实开发中,我们可能对打包的图片名字有一定的要求
  • 比如,将所有的图片放在一个文件夹中,跟上图片原来的名称,同时也要防止重复

image-20200921211143890

所以,我们可以在options中添加上如下选项:

  • img:文件要打包到的文件夹

  • name:获取图片原来的名字,放在该位置

  • hash:8:为了防止图片名称冲突,依然使用hash,但是我们只保留8位

  • ext:使用图片原来的扩展名

1
2
3
4
5
6
7
8
9
10
{
test: /\.(png|jpg|gif|jpeg)$/i,
use: [{
loader: 'url-loader',
options: {
limit: 11000,
name: 'img/[name].[hash:6].[ext]',
},
}, ],
},

但是,我们会发现图片并没有显示出来,这是因为图片使用的路径不正确

  • 默认情况下,webpack会将生成的路径直接返回给使用者

  • 但是,我们整个程序是打包在dist文件夹下的,所以这里我们需要在路径下再添加一个dist/,即在output选项里添加publishPath:’dist/‘

1
2
3
4
5
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: 'dist/'
},

ES6语法处理babel-loader

安装

1
npm install --save-dev babel-loader@7 babel-core babel-preset-es2015

配置

1
2
3
4
5
6
7
8
9
10
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
}

之后就可以看到打包后ES转成ES5的语法了

webpack配置vue🔺

配置

当我们使用上vue后,你会发现,那些页面渲染不出来且报出了一个这样的错误

image-20200922084012737

这是一个关于runtime-only和runtime-compiler之间的问题,后面会有补充

这个时候,你就需要在webpack的配置文件配置下面的代码

1
2
3
4
5
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
},

ok,问题就解决了

单文件组件🔺

先放上官网对其介绍

在很多 Vue 项目中,我们使用 Vue.component 来定义全局组件,紧接着用 new Vue({ el: '#container '}) 在每个页面内指定一个容器元素。

这种方式在很多中小规模的项目中运作的很好,在这些项目里 JavaScript 只被用来加强特定的视图。但当在更复杂的项目中,或者你的前端完全由 JavaScript 驱动的时候,下面这些缺点将变得非常明显:

  • 全局定义 (Global definitions) 强制要求每个 component 中的命名不得重复
  • 字符串模板 (String templates) 缺乏语法高亮,在 HTML 有多行的时候,需要用到丑陋的 \
  • 不支持 CSS (No CSS support) 意味着当 HTML 和 JavaScript 组件化时,CSS 明显被遗漏
  • 没有构建步骤 (No build step) 限制只能使用 HTML 和 ES5 JavaScript,而不能使用预处理器,如 Pug (formerly Jade) 和 Babel

文件扩展名为 .vuesingle-file components (单文件组件) 为以上所有问题提供了解决方法,并且还可以使用 webpack 或 Browserify 等构建工具。

简单的来讲,为了避免全局变量污染,避免多个子组件的存在导致代码易读性变得非常差,和CSS样式被遗漏,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
<template>
<div>
<h1 class="aaa">我是子组件</h1>
<p>{{message}}</p>
</div>
</template>

<script>
//将配置输出
export default {
data() {
return {
message: 'woshizizujian'
}
}
}
</script>

<style scoped>
.aaa {
color: yellow;
font-size: 20px;
}
</style>

父节点代码

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
<template>
<div>
<h2 class="title">{{message}}</h2>
<cpn></cpn>
</div>
</template>

<script>
//将子组件引入页面
import cpn from './cpn.vue'
//输出自身配置
export default {
components: {
cpn
},
data() {
return {
message: '你好啊'
}
},
}
</script>

<style>
.title {
color: orange;
}
</style>

main.js最终引用并简单配置

1
2
3
4
5
6
7
8
9
10
11
import Vue from 'vue'

import App from './src/vue/App.vue'

new Vue({
el: '#app',
template: '<App/>',
components: {
App
}
})

主页面在需要用到组件的地方定义其绑定的标签即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>

<body>
<h2>大家好啊?</h2>
<div id="img"></div>
<div id="app"></div>
<script src="./dist/bundle.js"></script>
</body>

</html>

当然,你想要解析vue文件,你还需要安装对应的loader

  • vue-loader
  • vue-template-compiler
1
npm i --save-dev vue-loader vue-template-compiler

然后就是配置啦,我这里因为vue-loader装的是13的版本,所以就不需要具体做它的相关插件配置,下面是vue-template-compiler(渲染vue)和vue-loader(解析vue)的相关配置

1
2
3
4
{
test: /\.vue$/,
use: ['vue-loader']
}

关于关注点分离

还是先放出官网的介绍

一个重要的事情值得注意,关注点分离不等于文件类型分离。在现代 UI 开发中,我们已经发现相比于把代码库分离成三个大的层次并将其相互交织起来,把它们划分为松散耦合的组件再将其组合起来更合理一些。在一个组件里,其模板、逻辑和样式是内部耦合的,并且把他们搭配在一起实际上使得组件更加内聚且更可维护。

将页面拆分成多个组件,导航栏、搜索栏、底部栏等等,这既降低了组件的耦合度,又提高了组件的可复用性,更加适合我们的惯用思维,将大的问题拆成多个小的问题,一一解决,最后解决掉大问题,而这些处理掉小问题的解决方法又可以在将来的某个时候直接用上,何乐而不为呢

webpack-plugin

简介

  • plugin是什么?

    • plugin是插件的意思,通常是用于对某个现有的架构进行扩展。

    • webpack中的插件,就是对webpack现有功能的各种扩展,比如打包优化,文件压缩等等。

  • loader和plugin区别

    • loader主要用于转换某些类型的模块,它是一个转换器。

    • plugin是插件,它是对webpack本身的扩展,是一个扩展器。

  • plugin的使用过程:

    1. 通过npm安装需要使用的plugins(某些webpack已经内置的插件不需要安装)

    2. 在webpack.config.js中的plugins中配置插件

添加版权信息

这是webpack自带的一个插件,如何使用呢

  1. 首先在webpack.config.js文件中引入webpack

    1
    const webpack = require('webpack')
  2. 使用

    1
    2
    3
    plugins: [
    new webpack.BannerPlugin('我是版权信息哈哈哈')
    ]
  3. 结果显示

    image-20200923110703505

打包html

简介: 可以将你定义的html模板在生产环境下打包并生成html文件,且会自动绑定bundle.js文件

安装:

1
npm install html-webpack-plugin --save-dev

使用:

  1. 引用

    1
    const HtmlWebpackPlugin = require('html-webpack-plugin')
  2. 配置

    1
    2
    3
    4
    5
    6
    plugins: [
    new webpack.BannerPlugin('我是版权信息哈哈哈'),
    new HtmlWebpackPlugin({
    template: "main.html" //模板
    }),
    ],

    js压缩

简介:对js等文件进行压缩处理

安装:

1
npm install uglifyjs-webpack-plugin@1.1.1 --save-dev

使用

  1. 引入

    1
    const Uglifyjs = require('uglifyjs-webpack-plugin')
  2. 配置

    1
    2
    3
    plugins: [
    new Uglifyjs(),
    ],

    很简单有没有,不过在最新的vue版本,已经可以自动压缩了,所以看看就可以了

vue搭建本地服务器

简介:webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js搭建,内部使用express框架,可以实现我们想要的让浏览器自动刷新显示我们修改后的结果。

安装

1
npm install --save-dev webpack-dev-server@2.9.1

使用(不需要引入模块,直接进行配置,注意:它和entry、output等是并列关系)

1
2
3
4
5
devServer: {
contentBase: './dist/',
inline: true,
port: 3333
}

相关参数说明:

  • contentBase:为哪一个文件夹提供本地服务,默认是根文件夹,我们这里要填写./dist

  • port:端口号

  • inline:页面实时刷新

  • historyApiFallback:在SPA页面中,依赖HTML5的history模式

在package.json文件中可以定义–open直接打开该服务

1
"dev": "webpack-dev-server --open"

webpack配置文件的分离

在开发中,一般大型的项目会需要配置很多plugin插件以及很多loader以及自身的相关配置,到后期整个文件会越来越大,代码可阅读性也会变得越来越差,并且,开发环境的有些配置在生产环境中是不需要的,所以我们可以按照基础必备配置、生产环境配置以及开发环境配置进行配置抽离,分为三个文件,并使用build文件夹将他们包裹起来

  1. base.config.js
  2. prod.config.js
  3. dev.config.js

image-20200923113605570

但是首先,你需要安装相关的npm插件用来合并vue配置文件

1
npm install webpack-merge --save-dev

引入

1
const webpackMerge = require('webpack')

然后对之前的整个配置文件分离后可得到下面的各个配置base.config.js

dev.config.js

1
2
3
4
5
6
7
8
9
const webpackMerge = require('webpack-merge')
const baseConfig = require('./base.config')

module.exports = webpackMerge(baseConfig, {
devServer: {
contentBase: './dist',
inline: true
}
})

修改base.config.js的打包输出路径, path: path.join(__dirname, '../dist')

1
2
3
4
5
6
7
8
module.exports = {
entry: './main.js',
output: {
path: path.join(__dirname, '../dist'),
filename: 'bundle.js',
// publicPath: 'dist/'
}
}

配置package.json文件,修改生产环境的运行代码和开发环境的运行代码

1
2
"build": "webpack --config ./build/prod.config.js",
"dev": "webpack-dev-server --open --config ./build/dev.config.js"

打包文件解析

app.js

当前应用程序开发的所有代码

mainifest

打包代码的底层支持~联想一下CommonJS AMD CMD标准

使浏览器能识别那些导出导入规则

vendor
第三方框架(bs\axios\vue)代码