Vue全家桶--11 Webpack和Vue-Loader打包资源
11.1 Webpack介绍
11.1.1 Webpack是什么
Webpack 是一个前端的静态模块资源打包工具,能让浏览器也支持模块化。它将根据模块的依赖关系进行静态分
析,然后将这些模块按照指定的规则生成对应的静态资源。
11.1.2 Webpack作用
**Webpack 核心主要进行 JavaScript 资源打包
**如下图,它可以结合其他插件工具,将多种静态资源css、png、sass 分类转换成一个个静态文件,这样可以减
少页面的请求。
**可集成 babel 工具实现 EcmaScript 6 转 EcmaScript 5 ,解决兼容性问题
**可集成 http 服务器
**可集成模块热加载,当代码改变后自动刷新浏览器 等等功能
11.1.3 参考资料
webpack1 和 webpack2+ 版本变化很大,基本上推倒重来, webpack1 目前已经基本不用了。
**webpack1 官网 https://webpack.github.io/
**webpack2.x 英文官网 https://webpack.js.org/
**webpack2.x 中文官网 https://webpack.docschina.org/
**webpack2.x 指南文档:https://webpack.docschina.org/guides/
11.2 Webpack安装和案例
11.2.1 全局安装
1. 安装 webpack
安装最新版本 npm install --global webpack 或者 安装特定版本 npm install --global webpack@<version>
2. 如果上面安装的是 webpack v4+ 版本, 还需要安装 CLI , 才能使用 webpack 命令行
npm install --1 global webpack-cli
npm install --1 global webpack-cli
3. 如果安装后,命令行窗口 webpack 命令不可用,则手动配置 全局目录的 环境变量
11.2.2 快速入门
VSCode 中安装插件 Node Snippets ,有代码快捷提示
(1)打包JS模块
1.1 全局安装 webpack@v4.35.2 与 webpack-cli@3.3.6
npm i -g webpack@v4.35.2
npm i -g webpack-cli@3.3.6
1.2 安装后查看版本号。如果有红色字提醒,没关系忽略它
webpack -v
1.3 如果安装后,命令行窗口 webpack 命令不可用,则配置环境变量
参考网址:https://www.cnblogs.com/steamed-twisted-roll/p/11299429.html
1.4 创建以下目录结构和文件
webpack-demo1 |— index.html |— js |- bar.js |— main.js
1.5 bar.js
// node 模块化编程, 导出函数 module.exports = function () { console.log('我是 bar 模块') }
1.6 main.js
var bar = require('./bar') // 可省略 .js 后缀名 bar() // 调用 bar.js 中的函数
1.7 node 运行 js 模块,注意命令执行所在目录: WebStudywebpack-demo1
1.8. index.html 文件引入 main.js , 如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <script src="./js/main.js"></script> </body> </html>
1.9 访问index.html,浏览器无法识别JS模块化文件
**Node 执行是可以的
1.10 打包 JS,注意命令执行所在目录: WebStudywebpack-demo1 ,不要少了 -o
命令: webpack 模块入口文件路径 -o 模块出口文件路径
1.11 将 index.html 引入的 JS 文件改为打包之后,浏览器可以识别的 JS目标文件
<body> <script src="./js/bundle.js"></script> </body>
1.12 输出正常
(2)改造目录结构
(3)打包配置文件webpack.config.js
// 引用 Node.js 中的 path 模块,处理文件路径的小工具 const path = require("path"); // 1. 导出一个webpack具有特殊属性配置的对象 module.exports = { // 指定模式配置,取值: none(什么也没有), development or production(默认的) // 如, production 模式打包后 bundle.js是压缩版本的, development则不是压缩的 mode:'none', // 入口 entry: './src/main.js', // 入口模块文件路径 // 出口是对象 output: { // path 必须是一个绝对路径 , __dirname 是当前js的绝对路径: //D: StudentProjectWebStudywebpack- demo2 path: path.join(__dirname, './dist/'), // 打包的结果文件存储目录 filename: 'bundle.js' // 打包的结果文件名 } }
11.2.3总结全局安装
不推荐 全局安装 webpack。全局安装的 webpack ,在打包项目的时候,使用的是你安装在自己电脑上的
webpack,如果项目到了另一个人的电脑上,他可能安装的是旧版本 webpack。那么就可能涉及兼容性的问题。而
且如果他没有在全局安装 webpack 则就无法打包。
所以,为了解决以上的问题,官方推荐本地安装 webpack,就是将 webpack 安装到对应项目中。这样项目到哪里,
webpack 就跟到哪里(webpack 打包工具随着项目走)。
11.2.4 本地安装(推荐)
本地安装的时候,建议把 webpack 安装到 devDependencies 开发依赖 ( --save-dev ) 中,因为 webpack 只是一个打包工具,项目如果需要上线,上线的是打包的结果,而不是这个工具。
所以我们为了区分生产环境和开发环境依赖,通过 --save (生产环境)和 --save-dev (开发环境)来区分。
本地安装命令
安装最新版本 npm install --save-dev webpack 安装特定版本 npm install --save-dev webpack@<version>
Demo:
a. 先把全局安装的 webpack 和 webpack-cli 卸载掉
npm uninstall -g webpack
npm uninstall -g webpack-cli
b. 安装 webpack@v4.35.2 与 webpack-cli
# 1. 进入到 webpack-demo3 cd d:StudentProjectWebStudywebpack-demo3 # 2. 初始化项目 `-y` 是采用默认配置 npm init -y # 3. 安装 v4.35.2 ,不要少了 v npm i -D webpack@v4.35.2 # 安装 CLI npm i -D webpack-cli@3.3.6
c. 在本地安装的 webpack ,要通过在项目文件夹下 package.json 文件中的 scripts 配置命令映射
"scripts": { "show": "webpack -v", "start": "node ./src/main.js", "build": "webpack" },
d. 然后再通过 npm run 命令别名 执行对应命令
npm run show
npm run build
npm run start
npm start
11.3 EcmaScript 6 模块规范
导出模块 export (等价于 module.exports)
导入模块 import (等价于 require)
ES6参考文档:https://es6.ruanyifeng.com/
11.3.1 导出默认成员
语法:默认成员只能有一个,不然会报错
export default 成员
// 导出函数 /* module.exports = function () { console.log('我是 bar 模块---Node') } */ // ES6 , 导出一个默认成员(任意类型),一个js中只能有一个 default。可以默认导出任意类型成员 /* export default function (){ console.log('我是 bar 模块---ES6') } */ // export default 'hello' export default { name: 'mxg' }
11.3.2 导入默认成员
import xxx from 模块文件
// Node 导入模块 // var bar = require('./bar') // bar() // ES6 导入 // 默认加载的是 export default 成员 import bar from './bar' // bar() // console.log( bar )
11.3.3 导出非默认成员
语法:非默认成员必须要有成员名称
export 成员
export const x = 'xxx'; export const y = 'yyy'; export function add(a, b) { return (a - 0) + (b - 0); }
错误提示:
//没有变量名,错误的 export 'xxx' // 没有函数名,错误的 export function (a, b) { return a + b }
11.3.4 导入非默认成员
// 方式一:按需导入指定成员,采用 解构赋值 的方式 import {成员名1, 成员名2, ..., 成员名n} from 模块文件 // 方式二:一次导入模块文件中的所有成员(包含 default 成员) import * as 别名 from 模块文件
// 通过 export xxx 导出的非默认成员,可使用 解构赋值 的方式按需加载成员 // x 对应就是 bar.js 中的 x 成员,y 对应就是 bar.js 中的 y 成员, import {x, y, add} from './bar' console.log(x, y, add(10, 20)) // 一次性加载 export xxx 导出所有成员, 不采用解构赋值 import * as bar2 from './bar' console.log(bar2)
11.4 打包 CSS/Images等资源
11.4.1 打包CSS资源
(1)安装 style-loader 和 css-loader 依赖
css-loader 是 将 css 装载到 javascript;
style-loader 是让 javascript 认识 css。
npm install --save-dev style-loader css-loader
(2)修改 webpack.config.js 文件
// 引用 Node.js 中的 path 模块,处理文件路径的小工具 const path = require("path"); // 1. 导出一个webpack具有特殊属性配置的对象 module.exports = { // 指定模式配置,取值: none(什么也没有), development or production(默认的) // 如, production 模式打包后 bundle.js是压缩版本的, development则不是压缩的 mode: 'none', // 入口 entry: './src/main.js', // 入口模块文件路径 // 出口是对象 output: { // path 必须是一个绝对路径 , __dirname 是当前js的绝对路径: //D: StudentProjectWebStudywebpack- demo2 path: path.join(__dirname, './dist/'), // 打包的结果文件存储目录 filename: 'bundle.js' // 打包的结果文件名 }, module: { // 模块 rules: [ // 规则 { test: /.css$/, // 正则表达式,匹配 .css 文件资源 use: [ // 使用的 Loader ,注意顺序不能错 'style-loader', 'css-loader' ] } ] } }
(3)在src文件夹创建 css 文件夹, css文件夹下创建 style.css
body { background-color: cadetblue; }
(4)在 main.js 只引入 style.css
import './css/style.css'
(5)重新打包编译,index.html显示上述效果!
npm run build
****原理:
F12查看 index.html 源码后,其实是将 CSS 文件内容转成一个 JavaScript 模块,然后在运行 JavaScript 时,会将样式动态使用 <sytle> 标签作用在页面<head> 标签下
11.4.2 打包Images资源
(1)安装 file-loader 依赖
npm install --save-dev file-loader
(2)修改 webpack.config.js
// 引用 Node.js 中的 path 模块,处理文件路径的小工具 const path = require("path"); // 1. 导出一个webpack具有特殊属性配置的对象 module.exports = { // 指定模式配置,取值: none(什么也没有), development or production(默认的) // 如, production 模式打包后 bundle.js是压缩版本的, development则不是压缩的 mode: 'none', // 入口 entry: './src/main.js', // 入口模块文件路径 // 出口是对象 output: { // path 必须是一个绝对路径 , __dirname 是当前js的绝对路径: //D: StudentProjectWebStudywebpack- demo2 path: path.join(__dirname, './dist/'), // 打包的结果文件存储目录 filename: 'bundle.js' // 打包的结果文件名 }, module: { // 模块 rules: [ // 规则 { test: /.css$/, // 正则表达式,匹配 .css 文件资源 use: [ // 使用的 Loader ,注意顺序不能错 'style-loader', 'css-loader' ] }, { test: /.(png|svg|jpg|gif)$/, use: [ 'file-loader' ] } ] } }
(3) 打包编译
注意点:
如果直接访问根目录下的 index.html ,那么图片资源路径就无法访问到。
解决方案:就是把 index.html 放到 dist 目录中。
但是 dist 是打包编译的结果,而非源码,所以把 index.html 放到 dist 就不合适。
而且如果我们一旦把打包的结果文件名 bundle.js 改了之后,则 index.html 也要手动修改。
综合以上遇到的问题,可以使用一个插件: html-webpack-plugin 来解决
11.4.3 html-webpack-plugin
安装插件
npm install --save-dev html-webpack-plugin
修改webpack.config.js
// 引用 Node.js 中的 path 模块,处理文件路径的小工具 const path = require("path"); // 引入插件 const HtmlWebpackPlugin = require('html-webpack-plugin'); // 1. 导出一个webpack具有特殊属性配置的对象 module.exports = { // 指定模式配置,取值: none(什么也没有), development or production(默认的) // 如, production 模式打包后 bundle.js是压缩版本的, development则不是压缩的 mode: 'none', // 入口 entry: './src/main.js', // 入口模块文件路径 // 出口是对象 output: { // path 必须是一个绝对路径 , __dirname 是当前js的绝对路径: //D: StudentProjectWebStudywebpack- demo2 path: path.join(__dirname, './dist/'), // 打包的结果文件存储目录 filename: 'bundle.js' // 打包的结果文件名 }, module: { // 模块 rules: [ // 规则 { test: /.css$/, // 正则表达式,匹配 .css 文件资源 use: [ // 使用的 Loader ,注意顺序不能错 'style-loader', 'css-loader' ] }, { test: /.(png|svg|jpg|gif)$/, use: [ 'file-loader' ] } ] }, // 配置插件 plugins: [ new HtmlWebpackPlugin({ // 此插件作用是将 index.html 打包到 bundle.js 所在目录中, // 同时也会在 index.html 中自动的 <script> 引入 bundle.js // 注意:其中的文件名 bundle 取决于上面output.filename中指定的名称 template: './index.html' }) ] }
打包编译,dist文件夹中就有了index.html,访问正常!
11.5 实时重新加载
11.5.1 说明
采用 webpack 提供的工具: webpack-dev-server ,它允许在运行时更新所有类型的模块后,而无需手动打
包和刷新页面,会自动打包和刷新页面。可以很大程度提高开发效率。
参考:https://webpack.docschina.org/guides/development/#使用-webpack-dev-server
11.5.2 实操
安装依赖
npm install --save-dev webpack-dev-server
修改 webpack.config.js 配置
// 引用 Node.js 中的 path 模块,处理文件路径的小工具 const path = require("path"); // 引入插件 const HtmlWebpackPlugin = require('html-webpack-plugin'); // 1. 导出一个webpack具有特殊属性配置的对象 module.exports = { // 指定模式配置,取值: none(什么也没有), development or production(默认的) // 如, production 模式打包后 bundle.js是压缩版本的, development则不是压缩的 mode: 'none', // 入口 entry: './src/main.js', // 入口模块文件路径 // 出口是对象 output: { // path 必须是一个绝对路径 , __dirname 是当前js的绝对路径: //D: StudentProjectWebStudywebpack- demo2 path: path.join(__dirname, './dist/'), // 打包的结果文件存储目录 filename: 'bundle.js' // 打包的结果文件名 }, module: { // 模块 rules: [ // 规则 { test: /.css$/, // 正则表达式,匹配 .css 文件资源 use: [ // 使用的 Loader ,注意顺序不能错 'style-loader', 'css-loader' ] }, { test: /.(png|svg|jpg|gif)$/, use: [ 'file-loader' ] } ] }, // 配置插件 plugins: [ new HtmlWebpackPlugin({ // 此插件作用是将 index.html 打包到 bundle.js 所在目录中, // 同时也会在 index.html 中自动的 <script> 引入 bundle.js // 注意:其中的文件名 bundle 取决于上面output.filename中指定的名称 template: './index.html' }) ], // 实时重新加载 devServer: { contentBase: './dist'//配置打包的目标路径 } }
修改package.json的scripts --open 选项打包成功,自动打开浏览器
"dev": "webpack-dev-server --open"
然后npm run dev 执行,就可以了
11.6 Babel浏览器兼容性
11.6.1 安装 Bable
npm install -D babel-loader @babel/core @babel/preset-env
11.6.2 配置 webpack.config.js
// 引用 Node.js 中的 path 模块,处理文件路径的小工具 const path = require("path"); // 引入插件 const HtmlWebpackPlugin = require('html-webpack-plugin'); // 1. 导出一个webpack具有特殊属性配置的对象 module.exports = { // 指定模式配置,取值: none(什么也没有), development or production(默认的) // 如, production 模式打包后 bundle.js是压缩版本的, development则不是压缩的 mode: 'none', // 入口 entry: './src/main.js', // 入口模块文件路径 // 出口是对象 output: { // path 必须是一个绝对路径 , __dirname 是当前js的绝对路径: //D: StudentProjectWebStudywebpack- demo2 path: path.join(__dirname, './dist/'), // 打包的结果文件存储目录 filename: 'bundle.js' // 打包的结果文件名 }, module: { // 模块 rules: [ // 规则 { test: /.css$/, // 正则表达式,匹配 .css 文件资源 use: [ // 使用的 Loader ,注意顺序不能错 'style-loader', 'css-loader' ] }, { test: /.(png|svg|jpg|gif)$/, use: [ 'file-loader' ] }, { test: /.m?js$/, exclude: /(node_modules|bower_components)/, // 排除的目录 use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] // 内置好的转译工具 } } } ] }, // 配置插件 plugins: [ new HtmlWebpackPlugin({ // 此插件作用是将 index.html 打包到 bundle.js 所在目录中, // 同时也会在 index.html 中自动的 <script> 引入 bundle.js // 注意:其中的文件名 bundle 取决于上面output.filename中指定的名称 template: './index.html' }) ], // 实时重新加载 devServer: { contentBase: './dist'//配置打包的目标路径 } }
11.7 Vue-Loader打包Vue单文件组件
11.7.1 打包Vue基本配置
安装 vue-loader 和 vue-template-compiler 依赖
npm install -D vue-loader vue-template-compiler
修改 webpack.config.js 配置
1.声明插件
const VueLoaderPlugin = require('vue-loader/lib/plugin');
2.模块规则,配置vue-loader
{ test: /.vue$/, loader:'vue-loader' }
3. 配置插件
new VueLoaderPlugin()
// 引用 Node.js 中的 path 模块,处理文件路径的小工具 const path = require("path"); // 引入插件 const HtmlWebpackPlugin = require('html-webpack-plugin'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); // 1. 导出一个webpack具有特殊属性配置的对象 module.exports = { // 指定模式配置,取值: none(什么也没有), development or production(默认的) // 如, production 模式打包后 bundle.js是压缩版本的, development则不是压缩的 mode: 'none', // 入口 entry: './src/main.js', // 入口模块文件路径 // 出口是对象 output: { // path 必须是一个绝对路径 , __dirname 是当前js的绝对路径: //D: StudentProjectWebStudywebpack- demo2 path: path.join(__dirname, './dist/'), // 打包的结果文件存储目录 filename: 'bundle.js' // 打包的结果文件名 }, module: { // 模块 rules: [ // 规则 { test: /.css$/, // 正则表达式,匹配 .css 文件资源 use: [ // 使用的 Loader ,注意顺序不能错 'style-loader', 'css-loader' ] }, { test: /.(png|svg|jpg|gif)$/, use: [ 'file-loader' ] }, { test: /.m?js$/, exclude: /(node_modules|bower_components)/, // 排除的目录 use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] // 内置好的转译工具 } } },{ test: /.vue$/, loader:'vue-loader' } ] }, // 配置插件 plugins: [ new HtmlWebpackPlugin({ // 此插件作用是将 index.html 打包到 bundle.js 所在目录中, // 同时也会在 index.html 中自动的 <script> 引入 bundle.js // 注意:其中的文件名 bundle 取决于上面output.filename中指定的名称 template: './index.html' }), new VueLoaderPlugin() ], // 实时重新加载 devServer: { contentBase: './dist'//配置打包的目标路径 } }
11.7.2 webpack与Vue单文件组件案例
目录结构
安装vue模块
npm i vue
index.html 单页面入口
<!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> <!-- 1. vue入口 --> <div id="app"></div> </body> </html>
App.vue 根组件
<template> <div> <h1>App 根组件</h1> <h2>{{ msg }}</h2> <!-- <Foo/> --> <foo></foo> <bar></bar> </div> </template> <script> // 要使用某个组件,要先导入再使用 import Foo from './components/Foo.vue' import Bar from './components/Bar.vue' // 导出一个默认的成员对象 ,它就是当前组件对象 , 可以直接在对象 中使用Vue中的选项,如data/methods/watch export default { data () { return { msg : 'hello mxg' } }, // template: 此选项不用写,因为上面template标签代表的就是当前的组件模板 // 引用子组件 components: { Foo, Bar } } </script> <style scoped> /* scoped 作用是将样式只作用到当前组件中,不然会传递到其他 父子组件中 */ h1 { color: red } </style>
main.js打包入口文件
import Vue from 'vue' import App from './App.vue' new Vue({ el: '#app', template: '<App />', components: {App} })
打包构建
npm run build
11.7.3 解决警告问题
// 通过这种方式引入的 vue模块,对应的是不是完整版(编译功能,运行功能) // 它默认引入的 vue模块中的package.json中main选项中引入的那个"main": "dist/vue.runtime.common.js" // 而这个默认引入的是运行版,没有编译功能,所以会发出警告 // import Vue from 'vue' // 解决:手动的引入指定的版本,正面引入的是完整版 // import Vue from 'vue/dist/vue.js' // 方式2 :采用webpack配置完整版vue.js import Vue from 'vue' import App from './App.vue' /* new Vue({ el: '#app', // template 实质上没有编译和渲染功能,而当前编译功能可以直接采用 vue-loader进行编译, // 而渲染功能实质上是通过render函数 来进行渲染组件,所以只需要在此处指定 render渲染组件即可 // template: '<App />', // <App></App> // render: function (h) { // h它是一个函数,这个函数用于要接收渲染的组件 // return h(App) //函数的返回值就是渲染的结果 // }, // 如果采用render进行渲染,则 main.js 当中的 components可以省略 // components: {App} // 箭头函数 简写1 // render: h => { // return h(App) // } // 箭头函数 简写2 render: h => h(App) }) */ new Vue({ render: h => h(App) }).$mount('#app')
11.7.4 .vue单文件组件完整版
App.vue
<template> <div> <h1>App 根组件</h1> <h2>{{ msg }}</h2> <!-- <Foo/> --> <foo></foo> <bar></bar> </div> </template> <script> // 要使用某个组件,要先导入再使用 import Foo from './components/Foo.vue' import Bar from './components/Bar.vue' // 导出一个默认的成员对象 ,它就是当前组件对象 , 可以直接在对象 中使用Vue中的选项,如data/methods/watch export default { data () { return { msg : 'hello mxg' } }, // template: 此选项不用写,因为上面template标签代表的就是当前的组件模板 // 引用子组件 components: { Foo, Bar } } </script> <style scoped> /* scoped 作用是将样式只作用到当前组件中,不然会传递到其他 父子组件中 */ h1 { color: red } </style>
Foo.vue
<template> <!-- 一个vue文件中有且仅有一个template --> <div> <h1>我是 Foo 子组件1111</h1> <h2>我是 Foo子组件 h22222</h2> </div> </template> <script> // 如果 不写js代码,可以不需要 <script> // 一般采用的是导出一个默认成员 对象 export default { } </script> <style scoped> /* 如果 不写样式代码,可以不需要 <style> */ /* scoped 作用是将样式只作用到当前组件中,不然会传递到其他 父子组件中 */ h2 { color: blue } </style>
11.7.5 模块热替换(HMR)
// 引入node中的path模块,处理文件路径 的小工具 const path = require('path') // 引入插件 const HtmlWebpackPlugin = require('html-webpack-plugin') // 1. 引入 vue-loader插件 const VueLoaderPlugin = require('vue-loader/lib/plugin') // 1. 导入webapck, 热模块加载 const webpack = require('webpack') // 导出一个webpack具有特殊属性配置的对象 module.exports = { mode: 'none', // 指定模式配置:"development" | "production" | "none" // 入口 entry: './src/main.js', // 入口模块文件路径 // 出口 output: { // path: './dist/', 错误的,要指定绝对路径 path: path.join(__dirname, './dist/'), //打包的结果文件生成的目录要是绝对路径 filename: 'bundle.js' }, // 配置插件 plugins: [ new HtmlWebpackPlugin({ // 指定要打包的模板页面 // 就会将 index.html 打包到与 bundle.js 所在同一目录下面, // 同时在 index.html 中会自动的使用script 标签引入bundle.js template: './index.html' }), // 3. 请确保引入这个插件! new VueLoaderPlugin(), // 3. 配置热模块加载对象 new webpack.HotModuleReplacementPlugin() ], // 实时重新加载 devServer: { // 目标路径 contentBase: './dist', // 2. 开启热模块加载, hot: true }, module: { rules: [ //配置转换规则 { test: /.css$/, // 注意,不要有单引号,正则表达 式,匹配 .css 文件资源 use: [ // 根据外国人的习惯来的顺序,而且顺序不要写错 'style-loader', // js识别css 'css-loader' // css 转换为 js ] }, { test: /.(png|svg|jpg|gif)$/, use: [ 'file-loader' ] }, // 解决兼容性问题 { test: /.m?js$/, exclude: /(node_modules)/, // 排除的目录 use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] // babel中内容的转换规则工具 } } }, // 2. 处理 .vue 单文件组件 { test: /.vue$/, loader: 'vue-loader' } ] }, // 解析完整版 vue.js // resolve: { // alias: { // 'vue$': 'vue/dist/vue.js' // } // } }