为何有这篇文章
各个社区已经有无数篇帖子介绍如何使用webpack搭建前端项目,但无论是出于学习webpack的目的还是为了解决工作实际需要都面临着一个现实问题,那就是版本更新。别人的帖子可能刚写好版本就更新了,又要对着帖子找文档。
但这个过程十分重要,因为你想要的一切早已都在文档中给出了说明,在查阅文档的过程中不但能学会正确的使用工具更增强了阅读文档这种姿势正确的学习能力。
本文就是记录一次不断遭遇版本问题,一路查阅文档达到学习目的的经历,分享给大家。文章中每个章节提到的相关文档都会在本章节总结,在文章最后我还会将所有相关文档的链接进行汇总出来方便大家学习。
0. 安装webpack
第一个要查阅的就是webpack的官方文档,由于webpack近年来的使用广泛,中文文档早已跟上。
webpack官方文档:https://webpack.docschina.org/
我们可以在文档-指南中找到安装相关的内容。
webpack安装:https://webpack.docschina.org...
官方已说明不推荐全局安装webpack因为这样会将你项目中的webpack锁定到指定版本。不需要全局安装的可跳过此步骤
全局安装webpack:
npm i -g webpack
```
1. 项目初始化
初始化项目
在项目目录下初始化项目会得到package.json文件:
npm init
```
安装webpack和webpack-dev-server以及Vue
webpack4会依赖webpack-cli,如果你没有安装也不必担心,npm会直接报错提示你
webpack安装:https://webpack.docschina.org...
webpack-dev-server:https://webpack.docschina.org...
npm i webpack webpack-dev-server webpack-cli --save-dev
npm i vue --save
```
配置webpack.config.js
根目录下新建webpack的配置文件webpack.config.js。
配置 入口、出口路径、打包后文件名和devServer的相关配置项
使用一个webpack的配置文件:https://webpack.docschina.org...
var path = require('path');
var webpack = require('webpack');
module.exports = {
//项目入口文件
entry: './src/main.js',
output: {
//打包出口路径
path: path.resolve(__dirname, './dist'),
//通过devServer访问路径
publicPath: '/dist/',
//打包后的文件名
filename: 'main.js'
},
mode:'development',
devServer: {
historyApiFallback: true,
overlay: true
}
};
根目录下新建index.html(模板如下)
新建index.html作为项目的主体页面,留出入口文件,入口文件的路径为webpack打包后输出的路径。
```<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>webpack-vue</title>
<meta name="description" content="">
<meta name="keywords" content="">
</head>
<body>
<script src="/dist/main.js"></script>
</body>
</html>
<h3>新建入口文件</h3>
<p>新建在src目录用来存放各种组件和静态文件,在src目录下新建入口文件main.js</p>
<h2>2. webpack的准备工作</h2>
<p>为方便开发调试,打包以及后期的代码编写便捷,我们需要对webpack进行初步配置</p>
<h3>自定义npm命令</h3>
<p>package.json 增加 scripts:</p>
"scripts": {
"dev": "webpack-dev-server --hot --open",
"build": "webpack --mode production --progress --hide-modules"
},
<p>这一步的目的是简化在命令行中输入复杂指令的操作,如需执行上述两命令只需要在命令行执行:</p>
<pre><code class="coffeescript">npm run dev
npm run build
webpack-dev-server --hot --open
目的是开启一个本地服务,--hot
为热加载,可实视查看页面状态,--open
直接从浏览器打开
webpack --mode production --progress --hide-modules
是打包整个项目的指令,--mode production
是以生产模式打包,这样会得到体积更小的文件,有兴趣的同学可以试试不加这个指令,看看差距到底有多大。--progress
打印出编译进度的百分比值,hide-modules
隐藏关于模块的信息
webpack的所有命令行可以在 api-命令行接口(cli) 中找到
webpack命令行接口:https://webpack.docschina.org...
配置webpack解析(resolve)
webpack.config.js中增加:
resolve: {
//路径别名
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@':path.resolve(__dirname, './src'),
},
//路径别名自动解析确定的扩展
tensions: ['.js', '.vue', '.json']
},
resolve
是webpack关于解析的配置项,alias
允许你在项目中使用路径别名代替复杂的路径。
extensions
会让webpack自动查找特定后缀的文件,在项目中引入文件时将不必再书写文件后缀。
至于为何将vue的路径别名指向vue/dist/vue.esm.js
请看下一章节vue的内容。
webpack解析(resolve):https://webpack.docschina.org...
3. 引入Vue
这一步我们先暂不考虑使用Vue的单页面组件。
通过查看 node_modules/vue/dist/README.md 我们可以得到关于webpack配置的说明,引入vue实际上是引用 node_modules/vue/dist/vue.esm.js,有理有据安心使用,并且会告诉你如果你在使用webpack 1.0应该引用vue.common.js,这便是上一步配置路径别名的依据。
在index.html中添加挂载点dom(#app)
```<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>webpack-vue</title>
<meta name="description" content="">
<meta name="keywords" content="">
</head>
<body>
<div id="app">{{message}}</div>
<script src="/dist/main.js"></script>
</body>
</html>
<h3>在main.js中引入vue</h3>
import Vue from 'vue'
var app = new Vue({
el: "#app",
data: {
message: 'hello webpack!!'
}
})
<p>启动server,可以预览到页面显示 'hello webpack!!'。</p>
<pre><code class="coffeescript">npm run dev
4. 配置loader——使用 css/scss
webpack默认只能解析js文件,因此需要在webpack.config.js中配置相应的解析器。
安装scss和相应样式文件解析器
npm i node-sass css-loader vue-style-loader sass-loader –save-dev
```
关于webpack解析样式的文档比较散乱,通过粗略查阅,
css-loader
用来解析css文件,
style-loader
用来解析dom中通过<style></style>
注入的样式,
而vue-style-loader
是vue官方基于style-loader开发的适用于vue的样式解析,
sass-loader
用来解析sass/scss文件
webpack - loader:https://webpack.docschina.org...
vue-style-loader:https://www.npmjs.com/package...
在webpack.config.js中配置解析器
module: {
rules: [{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader'
]
},{
test: /\.scss$/,
use: [
'vue-style-loader',
'css-loader',
'sass-loader'
]
},
{
test: /\.sass$/,
use: [
'vue-style-loader',
'css-loader',
'sass-loader?indentedSyntax'
]
}]
},
以上配置意为:
.css
为后缀的文件使用css-loader
, vue-style-loader
解析
.scss
和.sass
为后缀的文件使用sass-loader
,css-loader
, vue-style-loader
解析
解析顺序是由下到上的
webpack - module:https://webpack.docschina.org...
webpack - sass-loader:https://webpack.docschina.org...
在项目中使用、引入样式(测试)
在src目录下新建目录assets/styles存放统一样式,新建index.scss,写一段scss代码:
$appColor:red;
#app{
color:$appColor;
}
<p>在main.js中引入样式:</p>
import ‘./assets/styles/index.scss’
<p>再次重启服务可以看到样式已经加载成功了</p>
<h2>5. 配置loader——使用图片</h2>
<p>同样webpack人无法解析图片格式的文件,需要把图片当做模块使用file-loader解析。</p>
<blockquote>file-loader:<a href="https://webpack.docschina.org/loaders/file-loader/" rel="nofollow noreferrer">https://webpack.docschina.org...</a>
</blockquote>
<h3>安装file-loader</h3>
<pre><code class="coffeescript">npm i file-loader --save-dev
在webpack.config.js中配置解析器
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}
在项目中引入图片(测试)
在src目录下按照代码中路径准备一张图片
Vue.component('myComponent', {
template: '<img :src="url" />',
data() {
return {
url: require('./assets/images/logo.png')
}
}
})
在index.html中使用该组件
```<div id="app">
{{message}}
<my-component></my-component>
</div>
<p>重启服务,图片应该被成功加载了。</p>
<h2>6. 配置loader——使用babel</h2>
<p>babel可以让我们在项目中*的使用es6语法,他会为我们将es6语法编译成浏览器普遍通用的es5语法。<br>在此之前我们必须要对babel有一定的了解:</p>
<blockquote>babel官方文档:<a href="https://babel.docschina.org/docs/en/usage" rel="nofollow noreferrer">https://babel.docschina.org/d...</a>
</blockquote>
<p>在babel的使用指南中我们看到安装babel需要安装的依赖和配置babel的方法。<br>并且我们得知一个噩耗,babel从7.0开始与之前版本有写不兼容了。如果你的package.json中babel的版本混乱,很容易造成配置失败。</p>
<h3>安装babel</h3>
<pre><code class="coffeescript">npm install --save-dev @babel/core @babel/cli @babel/preset-env
npm install --save @babel/polyfill
注意:
- 其中preset是babel的配置,babel-loader在webpack中解析babel中会用到,polyfill也是babel的一个依赖
- babel的6.0 和7.0不兼容,如果package.json中版本混乱建议降级活升级以统一版本
在webpack.config.js中配置解析器
babel的webpack配置:https://babel.docschina.org/s... 选择webpack
module增加一条rules:
{
test: /\.js$/, exclude: /node_modules/,
loader: "babel-loader"
}
入口文件更改为:
entry: ["@babel/polyfill", './src/main.js'],
新建配置文件.babelrc
注意查看关于env的配置文档,新版本写法有变化。
@babel/preset-env:https://babel.docschina.org/d...
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "entry"
}
]
]
}
检测es6语法(正常运行说明babel配置成功)
在src下新建util.js
export default function getData() {
return new Promise((resolve, reject) => {
resolve('ok');
})
}
main.js
//引入组件
import getData from './util';
var app = new Vue({
el: "#app",
data: {
message: 'hello webpack!!'
},
methods: {
async fetchData() {
const data = await getData();
this.message = data;
}
},
created() {
this.fetchData();
}
})
import getData from './util';
常见报错
-
Can't resolve 'babel-polyfill' in 'E:\vue\webpack-vue'
webpack的入口文件polyfill版本与配置写法不匹配
错误:entry: ["babel-polyfill", './src/main.js'],
正确:entry: ["@babel/polyfill", './src/main.js'],
-
regeneratorRuntime is not defined
未安装@babel/polyfill
npm i babel-polyfill –save
-
Plugin/Preset files are not allowed to export objects
webpack的报错,版本混乱,不兼容
清理package.json中的babel依赖,删除node_module,重新执行npm install,再次安装babel依赖时注意版本统一
7. 使用Vue单文件组件
vue-loader中介绍了如何使用Vue单文件组件:
vue-loader: https://vue-loader.vuejs.org/zh/
安装vue-loader及依赖
npm i vue-loader vue-template-compiler –save-dev
```
配置webpack.config.js
Vue单文件组件:https://vue-loader.vuejs.org/...
// webpack.config.js
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
module: {
rules: [
// ... 其它规则
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
plugins: [
// 请确保引入这个插件!
new VueLoaderPlugin()
]
}
准备单文件组件
在src目录下创建文件App.vue
在src目录下新建目录components用来存放单文件组件,新建文件header.vue
App.vue
```<template>
<div>
<home-header user="vict"></home-header>
</div>
</template>
<script>
import homeHeader from '@/components/header.vue'
export default {
name:'app',
components:{
homeHeader
}
}
</script>
<style lang="scss" scoped>
$txtColor:red;
.sg{
color:$txtColor
}
</style>
<p><strong>header.vue(子组件)</strong></p>
<template>
<div>
header
<span>color</span>
<img src="@/assets/images/logo.png">
{{this.message}} - {{this.user}}
</div>
</template>
<script>
export default {
name:'homeHeader',
data () {
return {
message:'hello world'
}
},
props: {
user:String
}
}
</script>
<style lang="scss" scoped>
span{
color:green
}
</style>
<p><strong>main.js</strong></p>
import Vue from 'vue'
import App from './App.vue'
import './assets/styles/index.scss'
new Vue({
render:h => h(App)
}).$mount('#app')
<h2>8. 解决静态文件</h2>
<p>在.vue中url路径将被解析成webpack模块请求,<br>这里建议在webpack.config.js中配置路径别名以方便在单文件组件中引用图片等静态资源<br>在单文件组件中可以这样使用:</p>
```<img src="@/assets/images/logo.png">
9. 注入Vue Router
vue-router: https://router.vuejs.org/zh/
安装VueRouter
npm i vue-router –save
```
在main.js的Vue实例中注入Router
import Vue from 'vue'
import App from './App'
//引入router组件
import router from './router/router'
import './assets/styles/reset.css'
new Vue({
//注入router
router,
render:h => h(App)
}).$mount('#app')
构建router组件
按照路径创建router文件:src/router/router.js
router.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Page1 from '@/components/page1'
import Page2 from '@/components/page2'
Vue.use(VueRouter)
export default new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes: [{
path: '/',
name:'home',
component: Page1
},{
path: '/page2',
name:'page2',
component: Page2
}]
})
常见的渲染失败的原因:
- router实例的路由配置项 routes,不要误写成 routers
- routes配置项中必须要有路径为’/’,否则渲染失败
使用router
在App.vue中可以直接使用<router-view>
和<router-link>
标签
10. 注入Vuex
安装vuex
npm i vuex –save
```
在main.js的Vue实例中注入store
import Vue from 'vue'
import App from './App'
import router from './router/router'
import store from './store/store'
import './assets/styles/reset.css'
new Vue({
router,
store,
render:h => h(App)
}).$mount('#app')
构建store目录
src/store/store.js
可按需要将state、actions、mutations拆分出来
store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
cur:'OFF',
},
actions: {
changeCur (ctx,cur) {
ctx.commit('changeCueMutations',cur)
}
},
mutations: {
changeCueMutations (state,cur) {
cur == 'OFF' ? state.cur = 'ON' :state.cur = 'OFF'
}
}
})
11. 打包发布
webpack.config.js中如果配置项:mode:'development', build的结果是未经过压缩的非常的大
修改 package.json 中的scripts,将打包时的mode切换成production
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server --hot --open",
"build": "webpack --mode production --progress --hide-modules"
},
文档汇总
webpack相关
webpack官方文档:https://webpack.docschina.org/
webpack安装:https://webpack.docschina.org...
webpack-dev-server:https://webpack.docschina.org...
webpack配置:https://webpack.docschina.org...
webpack命令行接口:https://webpack.docschina.org...
webpack解析(resolve):https://webpack.docschina.org...
webpack - loader:https://webpack.docschina.org...
webpack - module:https://webpack.docschina.org...
Vue相关
Vue官方文档(中文):https://cn.vuejs.org/
Vue-cli: https://cli.vuejs.org/zh/guide/
Vue-router: https://router.vuejs.org/
Vuex: https://vuex.vuejs.org/
Vue-loader(单文件组件): https://vue-loader.vuejs.org/
babel相关
babel官方文档:https://babel.docschina.org/
babel使用指南:https://babel.docschina.org/d...
babel的配置:https://babel.docschina.org/s...
babel-env:https://babel.docschina.org/d...
其他
npm官方:https://www.npmjs.com/
npm CLI(命令行指令):https://docs.npmjs.com/
vue-style-loader: https://www.npmjs.com/package...
node-sass: https://www.npmjs.com/search?...