1.当前项目存在的问题
- 存在大量静态文件不好管理,要修改一个地方的地址就要在项目中找哪些地方用了这个资源
- 包之间的引入顺序没办法做到百分之百正确,需要很大精力去整理
- 没有一个具体的流程告诉我们一个网页应该怎么架构
- 文件模块化程度不高,需要在很多地方写大量重复代码
- 一些 ES6 的新语法不能够兼容浏览器,在开发中带来不好的体验
- 网页加载速度慢,会在无意中发起很多的二次请求,所以只请求一次会解决很多问题
2. 什么是 webpack
- 是基于 Node.js 开发出来的前端构建工具
- webpack 是从一个项目的整个生命周期出发的,因此会使我们的开发更加规范化
- webpack 会将所有错综复杂的关系处理好,解决好依赖关系,并进行合并,减少不必要的网络请求加快页面的启动
3. webpack 的使用
webpack 的安装
- 可以使用以下命令进行全局安装
npm install webpack -g
- 也可以使用下面的命令进行项目中的安装
npm install webpack --save-dev
- 我们在开发的时候一般会建立以下几个目录
- src 目录:页面的源代码,一般在里面包含以下几个文件跟目录
- css 目录:用于存放 css 样式
- images 目录:用于存放项目用到的图片资源文件夹
- js 目录:用于存放页面的处理逻辑,工具函数等
- index.html 文件:用于作为页面的首页
- main.js 文件:用于写页面的主要逻辑,项目的 js 入口文件
- dist 目录:将来开发完成之后发布出来的文件,整个文件才是给用户的内容
- package.json 文件:用于描述整个项目的文件
- node_modules 目录:用于存放项目中用到的包文件
- package_lock.json 文件:用于存放项目中依赖的库的版本的文件,之后在其他地方开发相同的内容的话只需要在此文件存在的目录下敲击 npm install 命令即可将之前项目中用到的包依赖进行安装
- 其实还有很多目录没有涉猎到,在之后的开发中我们将会在用到的地方介绍其作用
通过 npm 安装 vue 框架
- 在项目根目录敲击 npm install vue 即可完成 vue 的安装
- 之后将项目结构构建好即可
- 就按照上面所说的目录结构进行构建
使用 webpack 的规范写代码
- webpack 不允许在 html 页面中引包,所有引包,引样式相关的操作都要 webpack 自己进行构建,因此不需要在 html 页面中引入样式文件跟脚本文件,这些我们都会在 main 函数中使用 import * from xxxx 的方式来引入
- 我们只需要将所有的东西都写在 main 文件里面即可,将来页面构建完成之后只需要请求 main 文件即可
语法知识点 1. 用 es6 的规范引入文件,es6 导入模块的方式
- 我们可以使用
import vue from 'Vue'
来引包 - 这非常类似于我们在 node.js 中的
let path=require('path')
- 也就是我们用 vue 变量来接收 Vue 这个包里面的内容
- 也就是我们同时进行了变量的声明跟赋值
- 之后在 vue 使用的地方我们就按正常的 vue 的使用来使用即可
- 我们在这个 main.js 里面写完代码之后需要在 index.html 里面请求一下这个资源
- 之后只用请求这一次就可以了
- 但是我们请求完在浏览器里面一跑起来发现显示浏览器不支持 import 语法
- 这里就到我们 webpack 出场了
- 我们在我们的项目根目录里面执行以下命令
webpack ./src/main.js ./dist/bundle.js
- 这条命令的作用是,全局运行 webpack 命令,并且用它来处理 src 目录下面的 main.js 文件,并将处理结果输出为./dist 目录下面的 bundle.js
- 之后我们在页面中请求这个 bundle.js 即可实现我们的 es6 脚本的执行
- 这种情况下因为没有 webpack 的配置文件,因此我们需要在 dist 文件目录下面自己构建一个 budle.js 文件
- 之后的文件会同级出现在此目录下面
- 但是这样就有一个很严重的问题就是我们每次需要打包的时候都需要运行
webpack ./src/main.js ./dist/bundle.js
来构建文件 - 所以我们之后就学会了配置 webpack 的配置文件,这里面包含了 webpack 的常用配置文件
- 在 webpack 的 4 版本以上的版本都需要安装 webpack-cli 才能进行构建,因此我们也要同时将 webpack-cli 进行安装
- 安装命令为:
npm install webpack-cli
才能进行后期的开发 - 一般配置文件必须要的内容有 entry 入口,output 出口
- output 里面还包含文件的名字 filename 属性跟 path 属性
- entry 对象只有 path 属性
- 配置文件里面我们可以使用 node.js 的语法来构建,也就是模块化处理,我们直接向外暴露一个配置对象就可以了
// 请求path核心模块
const path = require("path");
module.exports = {
entry: path.join(__dirname, "./src/index.js"),
output: {
path: path.join(__dirname, "./dist"),
filename: "bundle.js"
}
};
- 这样就完成了我们配置,这个配置文件的名称叫做 webpack.config.js 在项目的根目录下面
- 之后我们直接执行 webpack 命令就可以了,所产生的文件会出现在我们指定的目录下面
4. webpack dev server 的基本使用
- 我们要知道现在的项目中我们只要发生一次变化都要去重新执行一次 webpack 命令
- 这样做非常麻烦,所以我们非常需要一个实时检测我们的文档变化并只要发生变化就出发我们 webpack 命令的东西
- 我们需要先安装 webpack-dev-server
- 可以通过这个
npm install webpack-dev-server -D
命令将这个依赖安装到项目的开发依赖 - 安装完毕之后这个命令的用法跟 webpack 的用法一模一样
- 由于我们在项目中本地安装的 dev-server 因此我们不能将其作为我们的脚本命令直接用在 powershell 中
- 因为 dev-server 依赖 webpack,因此需要在开发环境依赖中安装 webpack 也就是需要在后面加上一个-D 来安装 webpack
- 所以我们就要在 package 配置对象中配置我们的命令
- 我们可以在 script 对象中添加
"dev":"webpack-dev-server --mode development"
属性,之后就会实现对我们的代码的监视 - 之后我们就可以启动 npm run dev 就可以监视了
- 在 webpack4 之后需要在后面加上模式选项才能打包
- 之后的打包工作都会由 webpack-dev-server 帮我们完成,需要知道的是 webpack-dev-server 生成的文件会在其打开的本地服务器根目录下一个叫 bundle.js 文件里面,不会直接打包到我们项目指定的位置,所以需要在页面请求的时候注意一下需要这样请求
<script src="/bundle.js"></script>
- dev-server 帮我们打包生成的文件并没有存放到实际的本地物理磁盘上,而是直接托管到电脑的内存里面了,所以在文件目录里面找不到这个被打包的文件
- 我们可以认为这个 dev-server 在浏览器中给我们创建了跟 src,dist 文件同级的一个虚拟目录,专门存放我们打包的内容,这样会非常快,并且会减少因为频繁保存编译带来的磁盘损耗
webpack-dev-server 常用的命令学习
- 需要传入在 package.json 文件里面的 dev 属性中,跟 webpack-dev-server 同级存放,只要加空格即可
- 打包完文件自动打开浏览器指令
--open
- 指定端口号打开
--port 3000
- 打开站点之后打开指定目录
--contentBase src
- 使用热重载方式打开
--hot
(也就是不用每次都打包,直接在原有的基础上修改,减少不必要的代码更新,对样式的更改甚至可以做到浏览器无刷新)
- 以上这些方式都需要在 package.json 文件里面操作,不免有点多了,我们有第二种配置的方式
- 我们可以以配置文件的形式放入到 webpack.config.js 里面
- 我们在 webpack 的配置对象里面再加一个 devServer 的属性,这个里面就可以对我们的配置进行修改了
- 里面的热更新会有点区别,需要三步来对其进行配置,
- 第一步是 hot:ture
- 第二部把 webpack 模块导进来,就用 require 就可以了
- 第三步在 entry,output,devServer 属性同级的 plugins(数组)配置插件属性中放一个
new webpack.HotModuleReplacementPlugin()
字段,这是一个热更新的模块对象
- 开发中更推荐第一种方式来配置
第一种配置方式
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server --mode development --open --port 3000 --contentBase src --hot"
}
第二种配置方式
//webpack配置文件
const path = require("path");
// 第二部,用于热更新的内容
const webpack = require("webpack");
module.exports = {
entry: path.join(__dirname, "./src/main.js"),
output: {
path: path.join(__dirname, "./dist"),
filename: "bundle.js"
},
devServer: {
open: true,
port: 3000,
contentBase: "src",
hot: true // 第一步,设置使用
},
plugins: [
new webpack.HotModuleReplacementPlugin() // 第三步,配置插件
]
};
- 在现在的开发中我们的 html 页面没有做到直接放在内存中更新,我们想要实现同样的效果的话需要借助 webpack 的 html-webpack-plugin 插件来实现,可以通过
npm install html-webpack-plugin -D
来进行安装 - 之后再我们 config 文件里面导入这个插件
const htmlWebPackPlugin=requie(html-webpack-plugin)
- 记住,只要是插件,都一定要在 config 文件中进行设置
- 向之前的方式在插件节点插入我们的插件即可,与之前的不同的是它有配置对象
- new htmlWebPackPlugin({}),第一个是指定根据谁来生成我们的模板页面
- 也就是
{template:path.join(__dirname,'./src/index.html')}
将来生成页面的时候会根据这个路径来生成 - 第二个配置项就是生成的文件名称,{filename:"index.html"}
- 如果我们不改这个名字的话,我们访问的页面就会是内存中的页面,并且内存中的页面默认请求内存中的 bundle.js,所以会给我们带来很多好处,如果改这个名字的话就会访问本地磁盘中的文件,并且本地磁盘中必须要有引用
- 所以他有两个显著好处,一是帮我们在内存中自动生成文件,第二个是默认帮我们自动获取内存中的 js
<!-- 不用去请求路径,直接在内存中自动加 -->
<!DOCTYPE html>
<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>Document</title>
</head>
<body></body>
</html>
//webpack.config.js
//webpack配置文件
const path = require("path");
// 第二部,用于热更新的内容
const webpack = require("webpack");
//html的自动构建插件
const htmlWebackPlugin = require("html-webpack-plugin");
module.exports = {
entry: path.join(__dirname, "./src/main.js"),
output: {
path: path.join(__dirname, "./dist"),
filename: "bundle.js"
},
devServer: {
open: true,
port: 3000,
contentBase: "src",
hot: true // 第一步,设置使用
},
plugins: [
new webpack.HotModuleReplacementPlugin(), // 第三步,配置插件
new htmlWebackPlugin({
template: path.join(__dirname, "./src/index.html"),
filename: "index.html"
})
]
};
document.write("all");
window.alert("okk");
- 这样开发大大减少了我们在文件操作上的精力
5. css 的打包
- 如果我们按传统的方式在页面中请求样式资源的话,我们就相当于回到了从前,就发起了二次请求
- webpack 可以帮我们减少这一步骤
- 我们可以直接在 main 文件中导入我们的样式文件,因为样式文件没有什么需要操作的内容,因此只要导入其文件就可以了,不用将其绑定到一个变量身上之类的
- 使用
import '../../main.css'
的方式来导入 - 直接在 main 文件的上方添加这一段即可完成导入
- 我们的 webpck 默认只能打包 js 类型的文件,我们直接放着一段代码我们会发现报错显示您可能需要适合的 loader 来处理这类文件
- 也就是说我们需要一个加载器来加载我们 css 文件
- 如果单纯只想加载 css 的话我们需要安装两个 loader,分别为 style-loader ,css-loader 都需要用 npm 进行安装
- 之后再打开 webpack 配置文件加入一个配置属性叫做 module{},在这个对象上有一个 rules 属性,这个属性是一个数组,这个数组中存放了所有第三方文件的匹配和处理规则
- 其中的每一个内容都是一个对象,分别有 test 属性,也就是试探属性,这是一个正则表达式的属性,用于定义匹配规则,比如我们想要匹配后缀名为.css 的话我们就需要给 test 传入匹配.css 字段的正则表达式
- 后面的 use 属性即为使用那个第三方文件来进行处理
plugins: [
new webpack.HotModuleReplacementPlugin(), // 第三步,配置插件
new htmlWebackPlugin({
template: path.join(__dirname, './src/index.html'),
filename: 'index.html'
})
],
module: {
rules: [{
test: /\.css$/,//两个斜杠中间是匹配的内容,第一个反斜杠表示遇到.就开始匹配,并以css结尾
use: ['style-loader', 'css-loader']
}]
}
- 我们的 loader 的调用规则是从右到左的,也就是会先交给 cssloader 去处理,并将处理结果交给 styleloader 处理,之后还有的话就继续处理
- 下面我们来配置处理 less 文件的 loader
- 我们只需要安装 less-loader 即可,还需要安装一个他的依赖项 less 包
- 之后就按之前的方式来完成即可
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
},
{
test: /\.less$/,
use: ["style-loader", "css-loader", "less-loader"]
}
];
}
下面我们来看一下配置 scss 文件的 loader
这个 loader 在安装的时候也需要一个依赖项为 node-sass,这个依赖项只有 cnpm 才能下载,因此务必要把 npm 设置成淘宝镜像
之后就是先安装 sass-loader,之后又安装 node-sass 这样的操作了
需要注意的是这里的 scss 文件是由 sass-loader 来加载的,因此后面在使用的时候引起警惕
6. url-loader 的使用
- 在日常开发中我们会遇到给页面中某个元素设置背景图片的场景,这个时候我们会在
background:url('xxx.jpg')
里面设置我们要获取的图片的地址,但是我们现在的 webpack 不能够处理这类请求, - 因此我们需要安装 url-loader 这一个加载器
- 这个加载器会帮我们对 url 请求的内容进行打包,通常 url 请求的内容格式有.jpg,.gif,.ttf,.svg 等等
- 这类资源身上还存在的一个问题就是命名冲突的问题,我们会遇到在两个地方使用名称相同的资源,但内容不一样,按照传统的方式我们会在两个目录里面存放这类资源,但是我们发现 webpack 打包出来后的文件都会将资源存放在我们的虚拟内存里面,这样就不允许我们有相同名称的资源
- 庆幸的是我们的 url-loader 就可以解决这个命名冲突问题 按照传统惯例,第一步就是安装我们的 loader,第二部就是在 module 里面定义我们的匹配规则
- 说到匹配规则,url-loader 会需要一些配置项,就是在 use 里面配置 url-loader 的时候需要将这个 loader 的配置项也配置进去
- 安装命令
npm install url-loader file-loader -D
同时它会依赖一个 file-loader 加载器,因此我们也需要在 url-loader 之后将 file-loader 安装进去,但在配置项内部不用对其进行配置 - 对配置项进行匹配:
{test: /\.(jpg|svg|png|bmp|jpeg)$/,use: 'url-loader'}
- 这样就解决了我们图片路径的请求问题,但是,我们后面发现在页面中的图片竟然是 base64 格式的
- 也就是说我们的 webpack 将我们的图片直接转换成 base64 直接放在页面中了,这样就不用二次请求
- 这样操作固然是有好处的,但是对于一些体积比较庞大的图片我们这样操作会很浪费我们的带宽资源
- 因此我们最好是在配置项里面设置一个阈值,用于对图片 的大小进行控制,如果超过这个阈值,我们就直接请求图片,如果不超过,我们就转为 base64
- 所以我们就要看 url-loader 的配置选项了,这些加载器的传参方式与 url 地址的传参方式非常类似,我们只需要在请求的内容后面用问号加我们的配置选线就可以了类似于
use:'url-loader?limit=1024'
之后超过这个阈值的图片就会请求图片资源,而不是 base64,但是我们发现我们请求到的图片名称直接改变了,竟然是一堆哈希值,所以我们就得出结论为,url-loader 默认会将图片名称进行重命名,并且用哈希值来代替,但是我们想要改变这种方式,又不想出现两个文件相同名称的情况怎么办 - 还好,我们的 url-loader 帮我们设置了这类问题的解决方法,我们可以对其 name 配置项进行配置
use:'url-loader?limit=1024&[name].[ext]'
- 这里的[name]是说保留原有的名称.[ext]表示保留原有的后缀,我们可以在这个前面再加一个[hash]用于表示还有哈希值名称,并将其接到前面我们可以规定哈希的长度为 8 位,即[hash:8]就可以完成了
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
},
{
test: /\.less$/,
use: ["style-loader", "css-loader", "less-loader"]
},
{
test: /\.scss$/,
use: [
{
loader: "style-loader" // creates style nodes from JS strings
},
{
loader: "css-loader" // translates CSS into CommonJS
},
{
loader: "sass-loader" // compiles Sass to CSS
}
]
},
{
test: /\.(jpg|png|bmp|gif|svg)/,
use: "url-loader?limit=2048&name=[hash:4]-[name].[ext]"
}
];
}
- url-loader 处理字体文件,也就是我们会有一些字体图标,这些内容我们就可以使用它来处理
- 我们可以使用以下 bootstrap 的字体图标,首先就是安装 bootstrap
- 安装命令:
npm install bootstrap -s
- 之后再页面直接用使用 bootstrap 的方式使用
- 但我们没有处理字体文件,因此会报错,所以我们需要在 module 里面处理字体文件,这类文件是由 url-loader 来进行处理的,因此我们哦欸之好配置选项即可
- 因为 bootstrap4 需要依赖 jqurey 跟 popper.js,因此需要将这两个也进行安装
npm install --save jquery popper.js
{test: /\.(ttf|eot|svg|woff|woff2)$/,use: 'url-loader'}
- 如果有不显示的情况,就把目录中的 node_modules 删掉,重新安装即可
- 需要知道的是 json 文件中不能有注释
7. webpack 中使用 Babel
- 我们在构架我们工程的时候经常会遇到需要使用 ES6 的新语法的场景
- 但是我们的 webpack 对于这类请求无能为力,webpack 只能处理部分 es6 代码
- 但是我们可以使用第三方编译器将我们用 ES6 语法写的内容编译出来,并交给 webpack 进行打包处理
- 今天我们要学习的这个 loader 就是能狗完成这一功能的 loader
- 就是 Babel,它可以将我们写的高级语法代码转换成低级的代码
- 按惯例,我们需要在工程依赖中安装 babel
- 我们需要两套代码来进行部署
- 第一套:
babel-core babel-loader babel-plugin-transform-runtime
,这套代码是有关编译器的包 - 第二套:
babel-preset-env babel-preset-stage-0
,这套代码是有关编译库的包 - 我们用的 preset 位 env,里面包含了最新的 es 语法
- 安装完成之后我们需要在我们的配置文件中添加一个新的匹配规则
{test:/\.js\$/,use:'babel-loader',exclude:/node_modules/}
- 这段代码的意义就是,遇到.js 文件就用 babel-loader 进行编译,但是我们的 node_modules 除外,这样就保证了不会对一些不必要的内容进行编译的情况
- 这些完成之后我们需要在根目录下面创建 Babel 的配置文件,也就是创建一个
.babelrc
的文件 - 这个文件内部是 json 格式,所以写入的时候需要遵循 json 的写法
- 这个配置文件的内部有以下属性
- presets:["env","stage-0"],我们之前安装的 preset 就要写在这个里面,我们可以安装自己的语法包,并配置在这个里面
- plugins:["transform-runtime"],我们只装了一个插件,那就是我们安装的内容里面带有 plugin 的那个,就是我们的 transform-runtime 这个插件
- 之后我们就可以在项目中直接使用我们的 es6 语法了