Vue系列 webpack
模块化开发是为了解决 全局变量命名冲突的问题
export/ import
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="aaa.js" type="module"></script>
<script src="bbb.js" type="module"></script>
<script src="mmm.js" type="module"></script>
</body>
</html>
a.js
var name = '我是小a'
var age = 18
var flag = true
function sum(num1, num2) {
return num1 + num2
}
if (flag) {
console.log(sum(10, 20));
}
// 1.导出方式一:
export {
flag, sum
}
// 2.导出方式二:
export var num1 = 100;
export var height = 188
// 3.导出函数/类
export function mul(num1, num2) {
return num1 * num2
}
export class Person {
run() {
console.log('running...');
}
}
//一个模块中包含某个的功能,我们并不希望给这个功能命名,而且让导入者可以自己来命名
//这个时候就可以使用export default
// 4.export default
// const address = '北京市'
// export {
// address
// }
// export const address = '北京市'
// const address = '北京市'
//
// export default address
export default function (argument) {
console.log(argument);
}
b.js
import {sum} from './aaa.js'
var name = '小红'
var flag = false
console.log(sum(100, 200));
c.js
// 1.导入的{}中定义的变量
import {flag, sum} from "./aaa.js";
if (flag) {
console.log('小明是天才, 哈哈哈');
console.log(sum(20, 30));
}
// 2.直接导入export定义的变量
import {num1, height} from "./aaa.js";
console.log(num1);
console.log(height);
// 3.导入 export的function/class
import {mul, Person} from "./aaa.js";
console.log(mul(30, 50));
const p = new Person();
p.run()
// 4.导入 export default中的内容
import addr from "./aaa.js";
addr('你好啊');
// 5.统一全部导入
// import {flag, num, num1, height, Person, mul, sum} from "./aaa.js";
import * as aaa from './aaa.js'
console.log(aaa.flag);
console.log(aaa.height);
Webpack
webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具
webpack安装
1.安装webpack首先需要安装Node.js,Node.js自带了软件包管理工具npm,这里我贴一下我安装的版本,8.9及以上即可,
D:\Program Files\nodejs
$ node -v
v12.16.3
2.安装webpack,这里我先指定版本号3.6.0,因为vue cli2依赖该版本
在终端直接执行webpack命令,使用的全局安装的webpack
当在package.json中定义了scripts时,其中包含了webpack命令,那么使用的是局部webpack
-g 全局安装
npm install webpack@3.6.0 -g
--save-dev 局部安装,是开发时依赖,项目打包后不需要继续使用的
npm install webpack@3.6.0 --save-dev
webpack基本使用
dist文件夹:distribution,打包的最终文件放在这里,发给服务器进行发布
src文件夹:就是源代码,模块化编写
./src/main.js:程序入口,打包的时候只要打包这个文件即可,loader会根据main.js把依赖都打包进去
./src/mathUtils.js:演示comomjs
./src/info.js:演示es6
./dist/bundle.js:打包后的文件
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script src="./dist/bundle.js"></script>
</body>
</html>
main.js
// 1.使用commonjs的模块化规范
const {add, mul} = require('./mathUtils.js')
console.log(add(20, 30));
console.log(mul(20, 30));
// 2.使用ES6的模块化的规范
import {name, age, height} from "./info";
console.log(name);
console.log(age);
console.log(height);
mathUtils.js
function add(num1, num2) {
return num1 + num2
}
function mul(num1, num2) {
return num1 * num2
}
module.exports = {
add,
mul
}
info.js
//es6的规范来导出
export const name = 'why';
export const age = 18;
export const height = 1.88;
打包命令:webpack ./src/main.js ./dist/bundle.js
D:\kangpc\笔记\博客园\Vuejs\webpack
$ webpack ./src/main.js ./dist/bundle.js
Hash: 32ad594a197305c20454
Version: webpack 3.6.0
Time: 65ms
Asset Size Chunks Chunk Names
bundle.js 3.65 kB 0 [emitted] main
[0] ./src/main.js 284 bytes {0} [built]
[1] ./src/mathUtils.js 148 bytes {0} [built]
[2] ./src/info.js 104 bytes {0} [built]
我们考虑一下,如果每次使用webpack的命令都需要写上入口和出口作为参数,就非常麻烦,有没有一种方法可以将这两个参数写到配置中,在运行时,直接读取呢?
当然可以,就是创建一个webpack.config.js文件
webpack的配置文件
固定的名字:webpack.config.js
entry:入口,可以是字符串/数组/对象
output:出口,通常是一个对象,里面有两个重要的属性path和filename
const path = require('path') //path模块是nodejs的语法
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, 'dist'), //path必须是一个绝对路径
filename: 'bundle.js'
},
}
有外部依赖的话要建package.json
npm init
package.json里面还有依赖的话,要npm install
npm install
局部安装webpackage
第一步,项目中需要安装自己局部的webpack
npm install webpack@3.6.0 --save-dev
第二步,通过node_modules/.bin/webpack启动webpack打包
node_modules\.bin\webpack
package.json
{
"name": "meetwebpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack" //先找本地的webpack,没有再用全局的,命令行的都是全局的,本地的要使用./node_modules/webpack
},
"author": "",
"license": "ISC",
"devDependencies": { //--save-dev生成
"webpack": "^3.6.0"
}
}
package.json的scripts中定义自己的执行脚本。
package.json中的scripts的脚本在执行时,会按照一定的顺序寻找命令对应的位置。
首先,会寻找本地的node_modules/.bin路径中对应的命令。
如果没有找到,会去全局的环境变量中寻找。
如何执行我们的build指令呢?
npm run build
loader
摘取官方解释中的三句话
loader 用于对模块的源代码进行转换。
loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript 或将内联图像转换为 data URL。
loader 甚至允许你直接在 JavaScript 模块中 import CSS 文件!
下面就来讲一下webpack打包处理css文件的流程
css-loader
只负责加载不解析,官方解释为:解析 CSS 文件后,使用 import 加载,并且返回 CSS 代码
npm install --save-dev css-loader
style-loader
负责解析到dom,将模块的导出作为样式添加到 DOM 中
npm install style-loader --save-dev
配置
把css-loader引用到webpack.config.js中
这里有一点很重要,就是use里面的列表是从右到左的顺序来打包的
const path = require('path')
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
}
]
}
}
再打包一下npm run build
,一定要记得切到当前项目的目录下!
D:\kangpc\笔记\博客园\Vuejs\webpack\03-webpack的loader (meetwebpack@1.0.0)
$ npm run build
> meetwebpack@1.0.0 build D:\kangpc\笔记\博客园\Vuejs\webpack\03-webpack的loader
> webpack
Hash: dcb3fbc6797633246ee2
Version: webpack 3.6.0
Time: 446ms
Asset Size Chunks Chunk Names
bundle.js 21 kB 0 [emitted] main
[0] ./src/main.js 303 bytes {0} [built]
[1] ./src/js/mathUtils.js 148 bytes {0} [built]
[2] ./src/js/info.js 79 bytes {0} [built]
[3] ./src/css/normal.css 1.1 kB {0} [built]
[4] ./node_modules/css-loader/dist/cjs.js!./src/css/normal.css 182 bytes {0} [built]
+ 3 hidden modules
less-loader 处理less文件
less-loader 加载less文件到内存
less解析less文件为css文件
npm install --save-dev less-loader less
file-loader图片文件处理
安装
npm install --save-dev url-loader
把url-loader
配置webpack.config.js
const path = require('path')
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: 'dist/'
},
module: {
rules: [
{
test: /\.css$/,
// css-loader只负责将css文件进行加载
// style-loader负责将样式添加到DOM中
// 使用多个loader时, 是从右向左
use: [ 'style-loader', 'css-loader' ]
},
{
test: /\.less$/,
use: [{
loader: "style-loader", // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "less-loader", // compiles Less to CSS
}]
},
{
test: /\.(png|jpg|gif|jpeg)$/,
use: [
{
loader: 'url-loader',
options: {
// 当加载的图片, 小于limit时, 会将图片编译成base64字符串形式.
// 当加载的图片, 大于limit时, 需要使用file-loader模块进行加载到dist文件夹下
limit: 13000,
// []里面是变量,webpack的语法
//img:文件要打包到的文件夹
//name:获取图片原来的名字,放在该位置
//hash:8:为了防止图片名称冲突,依然使用hash,但是我们只保留8位
//ext:使用图片原来的扩展名
name: 'img/[name].[hash:8].[ext]'
},
}
]
},
{
test: /\.js$/,
// exclude: 排除
// include: 包含
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
}
]
}
}
file-loader
不需要在webpack.config.js配置,安装即可
npm install --save-dev file-loader
babel-loader
打包的bundle.js文件里面有es6语法(const),会使得还不支持es6的浏览器不能够很好的运行我们的代码,这时候要使用babel将es6的语法转成es5
npm install --save-dev babel-loader@7 babel-core babel-preset-es2015
转换前后代码展示
首先是main.js里面的es6代码
// 1.使用commonjs的模块化规范
const {add, mul} = require('./js/mathUtils.js')
console.log(add(20, 30));
console.log(mul(20, 30));
然后这段代码编译到bundle.js里面后,转换成了es5的代码,如下
// 1.使用commonjs的模块化规范
var _require = __webpack_require__(4),
add = _require.add,
mul = _require.mul;
console.log(add(20, 30));
console.log(mul(20, 30));
webpack配置vue vue-loader
1.安装
npm install vue --save
这里不适用--save-dev是因为vue作为模块放在node_module中,并不是只有开发时使用,在运行时也要用
2.导入并挂载vue实例
main.js
// 5.使用Vue进行开发
import Vue from 'vue'
// import App from './vue/app'
import App from './vue/App.vue'
new Vue({
el: '#app',
data: {
message: 'hello webpack'
}
})
3.在index.html写vue实例的模板
<div id="app">
<h2>{{message}}</h2>
</div>
4.到这里我们npm run build会报错,还需要在webpack.config.js中配置resolve,resolve配置是解决一些路径等等的问题
module.exports = {
entry: ...
...
...
resolve: {
extensions: ['.js', '.css', '.vue'],
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
}
}
template替换el
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
</div>
<script src="./dist/bundle.js"></script>
</body>
</html>
main.js
new Vue({
el: '#app',
// 有app又有template,vue会把template替换el(即index.html里面的app)
template: `
<div>
<h2>{{message}}</h2>
<button @click="btnClick">按钮</button>
<h2>{{name}}</h2>
</div>
`,
// template: '<App/>',
// components: {
// App
// }
data: {
message: 'hello webpack',
name: 'kang'
},
methods: {
}
})
构建
npm run build
起本地服务
http-server -c-1
浏览器访问并查看dom结构,可以看到main里面的template把html里面的app整个替换了
下面我把main.js第5点的代码载改造下,把vue的代码抽出来到一个app.js里面,抽取后的main和app.js代码如下
main.js
// 1.使用commonjs的模块化规范
const {add, mul} = require('./js/mathUtils.js')
console.log(add(20, 30));
console.log(mul(20, 30));
// 2.使用ES6的模块化的规范
import {name, age, height} from "./js/info";
console.log(name);
console.log(age);
console.log(height);
// 3.依赖css文件
require('./css/normal.css')
// 4.依赖less文件
require('./css/special.less')
document.writeln('<h2>你好啊,李银河!</h2>')
// 5.使用Vue进行开发
import Vue from 'vue'
import App from './vue/app'
// import App from './vue/App.vue'
new Vue({
el: '#app',
template: '<App/>',
components: {
App
}
})
app.js
export default {
template: `
<div>
<h2>{{message}}</h2>
<button @click="btnClick">按钮</button>
<h2>{{name}}</h2>
</div>
`,
data() {
return {
message: 'Hello Webpack',
name: 'kang'
}
},
methods: {
btnClick() {
}
}
}
然后再npm run build
构建,运行http-server -c-1
但是到这里为止,template和js代码还没有分离,所以这里我们再创建一个文件App.vue,把模板代码分离
app.js
export default {
template: ``,
data() {
return {
message: 'Hello Webpack',
name: 'kang'
}
},
methods: {
btnClick() {
}
}
}
App.vue
<template>
<div>
<h2 class="title">{{message}}</h2>
<button @click="btnClick">按钮</button>
<h2>{{name}}</h2>
<Cpn/>
</div>
</template>
<script>
import Cpn from './Cpn'
export default {
name: "App",
components: {
Cpn
},
data() {
return {
message: 'Hello Webpack',
name: 'kang'
}
},
methods: {
btnClick() {
}
}
}
</script>
<style scoped>
.title {
color: green;
}
</style>
这里构建运行,会报错,因为vue文件无法加载,需要安装和配置vue的loader
安装
npm install vue-loader vue-template-compiler --save-dev
配置webpack.config.js
const path = require('path')
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: 'dist/'
},
module: {
rules: [
...
...
{
test: /\.vue$/,
use: ['vue-loader']
}
]
再次构建、运行 ok
webpack的plugin
添加plugin有两步,一个是安装plugin,另一个是配置到webpack.config.js
添加版权的plugin
在webpack.config.js中增加plugin配置
const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
// publicPath: 'dist/'
},
module: {
rules: [
{
test: /\.css$/,
// css-loader只负责将css文件进行加载
// style-loader负责将样式添加到DOM中
// 使用多个loader时, 是从右向左
use: [ 'style-loader', 'css-loader' ]
},
{
test: /\.less$/,
use: [{
loader: "style-loader", // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "less-loader", // compiles Less to CSS
}]
},
{
test: /\.(png|jpg|gif|jpeg)$/,
use: [
{
loader: 'url-loader',
options: {
// 当加载的图片, 小于limit时, 会将图片编译成base64字符串形式.
// 当加载的图片, 大于limit时, 需要使用file-loader模块进行加载.
limit: 13000,
name: 'img/[name].[hash:8].[ext]'
},
}
]
},
{
test: /\.js$/,
// exclude: 排除
// include: 包含
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
},
{
test: /\.vue$/,
use: ['vue-loader']
}
]
},
resolve: {
// alias: 别名
extensions: ['.js', '.css', '.vue'],
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
},
plugins: [
new webpack.BannerPlugin('最终版权归一言所有'),
new HtmlWebpackPlugin({
template: 'index.html'
}),
new UglifyjsWebpackPlugin()
]
}
打包html的plugin
目前,我们的index.html文件是存放在项目的根目录下的。
我们知道,在真实发布项目时,发布的是dist文件夹中的内容,但是dist文件夹中如果没有index.html文件,那么打包的js等文件也就没有意义了。
所以,我们需要将index.html文件打包到dist文件夹中,这个时候就可以使用HtmlWebpackPlugin插件
HtmlWebpackPlugin插件可以为我们做这些事情:
1.自动生成一个index.html文件(可以指定模板来生成)
2.将打包的js文件,自动通过script标签插入到body中
安装HtmlWebpackPlugin插件
npm install html-webpack-plugin --save-dev
使用插件,修改webpack.config.js文件中plugins部分的内容如下:
1.这里的template表示根据什么模板来生成index.html
2.另外,我们需要删除之前在output中添加的publicPath属性
3.否则插入的script标签中的src可能会有问题
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
</div>
</body>
</html>
webpack.config.js
const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
// publicPath: 'dist/' //这里注释掉是因为index.html已经放在dist文件夹下了
},
......
plugins: [
new webpack.BannerPlugin('最终版权归一言所有'),
new HtmlWebpackPlugin({
template: 'index.html' //根据当前目录的文件index.html作为模板来生成html
}),
]
}
js压缩的plugin
在项目发布前,必须对js等文件进行压缩处理,这里我们使用uglifyjs-webpack-plugin
1.1.1版本,和cli2保持一致
npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
配置到webpack.config.js,这里我不贴全部,只贴plugin
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
plugins: [
new webpack.BannerPlugin('最终版权归一言所有'),
new HtmlWebpackPlugin({
template: 'index.html' //根据当前目录的文件index.html作为模板来生成html
}),
new UglifyjsWebpackPlugin()
],
}
搭建本地服务器
通过webpack-dev-server
webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js搭建,内部使用express框架,可以实现我们想要的让浏览器自动刷新显示我们修改后的结果。
不过它是一个单独的模块,在webpack中使用之前需要先安装它,2.9.1对应cli2
npm install --save-dev webpack-dev-server@2.9.1
配置到webpack.config.js
module.exports = {
...
devServer: {
contentBase: './dist',
inline: true
}
}
devserver也是作为webpack中的一个选项,选项本身可以设置如下属性:
contentBase:为哪一个文件夹提供本地服务,默认是根文件夹,我们这里要填写./dist
port:端口号
inline:页面实时刷新
historyApiFallback:在SPA页面中,依赖HTML5的history模式
配置package.json的scripts,这里加上--open表示运行的同时打开浏览器访问刚起起来的服务
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"dev": "webpack-dev-server --open"
},
运行
第一种
D:\kangpc\笔记\博客园\Vuejs\webpack\05-webpack的Plugin (meetwebpack@1.0.0)
$ .\node_modules\.bin\webpack-dev-server
Project is running at http://localhost:8082/
webpack output is served from /
Content not from webpack is served from ./dist
Hash: 95013ce8605924a0ea2e
Version: webpack 3.6.0
Time: 9238ms
...
...
第二种,配置package.json的scripts后,就可以npm run dev
运行,然后一边修改代码一边调试,不需要run build
PS D:\kangpc\笔记\博客园\Vuejs\webpack\05-webpack的Plugin> npm run dev
> meetwebpack@1.0.0 dev D:\kangpc\笔记\博客园\Vuejs\webpack\05-webpack的Plugin
> webpack-dev-server --open
Project is running at http://localhost:8081/
webpack output is served from /
Content not from webpack is served from ./dist
webpack: wait until bundle finished: /
运行并自动打开浏览器访问本地服务
npm run dev --open
一般开始阶段不会进行js压缩,因为这样会丑化js代码不利于我们开发调试,所以相关配置需要注释,这是生产环境才需要的配置
new UglifyjsWebpackPlugin() //js压缩,丑化js代码 发步生产的时候使用
另一个是在通过webpack-dev-server搭建本地服务,这是开发环境才需要的配置
// devServer: {
// contentBase: './dist',
// inline: true
// }
通过http-server
安装
npm install http-server -g
运行
PS D:\kangpc\笔记\博客园\Vuejs\webpack\05-webpack的Plugin> http-server -c-1
Starting up http-server, serving ./
http://192.168.155.113:8081
http://10.129.0.99:8081
http://169.254.150.107:8081
http://169.254.206.168:8081
http://127.0.0.1:8081
Hit CTRL-C to stop the server
webpack配置文件的分离
分离的目的是为了开发阶段和发布阶段使用不同的配置文件
base.config.js:开发和生产都依赖的公共的配置
dev.config.js:开发阶段特有的配置
prod.config.js:生产环境特有的配置
也就是说我们在开发环境构建的时候需要base+dev,而在生产发布的时候需要base+prod,
webpack-merge 组件可以实现合并配置的需求,
安装
npm install webpack-merge
老规矩安装完plugin都要去webpack.config.js中添加引用和配置(当前这里没有配置,只有引用),当然现在不是webpack...而是上面分离后的配置文件
const webpackMerge = require('webpack-merge')
下面贴出来完整的配置文件
base.config.js
const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, '../dist'),
filename: 'bundle.js',
// publicPath: 'dist/'
},
module: {
rules: [
{
test: /\.css$/,
// css-loader只负责将css文件进行加载
// style-loader负责将样式添加到DOM中
// 使用多个loader时, 是从右向左
use: [ 'style-loader', 'css-loader' ]
},
{
test: /\.less$/,
use: [{
loader: "style-loader", // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "less-loader", // compiles Less to CSS
}]
},
{
test: /\.(png|jpg|gif|jpeg)$/,
use: [
{
loader: 'url-loader',
options: {
// 当加载的图片, 小于limit时, 会将图片编译成base64字符串形式.
// 当加载的图片, 大于limit时, 需要使用file-loader模块进行加载.
limit: 13000,
name: 'img/[name].[hash:8].[ext]'
},
}
]
},
{
test: /\.js$/,
// exclude: 排除
// include: 包含
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
},
{
test: /\.vue$/,
use: ['vue-loader']
}
]
},
resolve: {
// alias: 别名
extensions: ['.js', '.css', '.vue'],
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
},
plugins: [
new webpack.BannerPlugin('最终版权归aaa所有'),
new HtmlWebpackPlugin({
template: 'index.html'
})
]
}
dev.config.js
const webpackMerge = require('webpack-merge')
const baseConfig = require('./base.config')
module.exports = webpackMerge(baseConfig, {
devServer: {
contentBase: './dist',
inline: true
}
})
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()
]
})
现在我们把配置文件都做了分离,原来的webpack.config.js就可以删除了,删除后需要去package.json中重新指定新的配置文件,不然打包时候会报错提示你找不到webpack.config.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"
},