一、项目初始化
1.初始化项目
npm init -y
-
init: 完成项目初始化
-
-y: 使用默认选项
结果:会生成package.json文件
2.安装webpack
npm i webapck webpack-cli -D
-
-D: 安装开发坏境的依赖,是--save-dev的简写
结果:会发现多了 node_modules 目录, 在 package.json 文件中, 多了如下图所示的两个包
请注意,部分电脑以上安装可能会报错,怎么解决? ——分开安装
// 第一步:
npm i webpack-cli -D
// 第二步:
npm i webpack -D
二、编写项目入口
1.编写index.html
在项目根目录下创建一个 index.html,,作为项目主页文件,代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>从零开始打造前端项目</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
演示:
2.编写vue根实例
创建 src 目录. 在 src 目录下创建 main.js 作为项目的总入口文件
在 main.js 中, 完成如下操作
- 创建 vue 根实例
- 挂载 App 组件
1>安装vue
npm i vue
结果:会发现在 package.json 中多了下图所示的内容
2>创建 Vue 根实例
编写main.js
// 引入vue
import Vue from 'vue'
// 创建vue实例
new Vue({
el: '#app'
})
演示:
3>挂载app组件
step1:编写 app.vue 文件
<template>
<div>this is App</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style scoped></style>
演示:
step2:编写 app.vue 文件
在 main.js 中, 完成如下操作
- 导入 App 组件对象
- 挂载到 Vue 根实例
main.js
// 引入vue
import Vue from 'vue'
import App from './app.vue'
// 创建vue实例
new Vue({
el: '#app',
components: {App}
})
演示:
step3:使用 App 标签渲染
main.js
new Vue({
el: '#app',
components: { App },
template: '<App/>'
})
演示:
这里的 App 相当于在 components 中定义的组件名
3.引入main.js测试
在 index.html 文件中引入 main.js, 测试
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>从零开始打造前端项目</title>
</head>
<body>
<div id="app"></div>
<script src="./src/main.js"></script>
</body>
</html>
演示: 发现报错!
原因:浏览器不能解析
三、webpack基本配置
为了让浏览器能正确解析,我们需要引入webpack将源代码重新编译
1.创建 webpack 配置文件
在根目录下创建 webpack 配置文件 webpack.config.js, 编写最基本的配置
webpack.config.js
// 使用node的path模块
const path = require('path')
module.exports = {
mode: 'development',
// 打包入口
entry: './src/main.js',
// 打包出口
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
演示:
2.编写webpack执行命令
package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
}
演示:
接下来就可以通过npm run build
来执行 webpack 打包了
3.测试
在命令行中执行npm run build
演示:提示错误
原因:webpack 只能打包 js 文件. 对于后缀名为 vue 的文件不能打包. 如何解决? ——通过 vue-loader 来打包 vue 文件
四、使用vue-loader打包 vue 文件
vue-loader官网:https://vue-loader.vuejs.org/zh/guide/
1.安装 vue-loader
npm install -D vue-loader vue-template-compiler
演示:
安装完成后, 发现 vue-loader 依赖 css-loader, 因此我们也要手动安装一下 css-loader
2 安装 css-loader
npm install -D css-loader
演示:
3.webpack 配置
webpack.config.js
// 使用node的path模块
const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
mode: 'development',
// 打包入口
entry: './src/main.js',
// 打包出口
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
plugins: [
// 请确保引入这个插件!
new VueLoaderPlugin()
]
}
演示:
4 重新打包测试
重新打包, 发现在 dist 目录下就会生成 bundle.js文件
5 在index.html中引入 bundle.js
在 index.html 中引入 bundle.js 测试
演示:发现报错
原因:
Vue 源码被打包后,会生成几种适用不同规范性的文件(可在node_modules/vue/dist文件夹下查看):
而默认导出的是 vue.common.js,而此处我们运用到的是amd的方式 如何解决?
在 webpack.config.js 中, 添加别名的配置
延伸:
amd
规范因RequireJS而出名,其适用于浏览器端,可通过script标签引入commonjs
语法类似node,因为node使用commonjs规范umd
是amd和commonjs的统一规范,支持两种规范,即写一套代码,可用于多种场景esm
一般通过import引入,也能通过设置type=module,用于html中,现在很多浏览器开始支持
resolve: {
alias: {
'vue': 'vue/dist/vue.js',
// 将../src映射为'@', 后面会用到,这里先不管
'@': require('path').resolve(__dirname, '../src')
}
}
演示:
重新打包测试:(运行正常)
总结:
- webpack 本身只能打包 js 文件, 如果要打包其他文件就需要借助于 loader
- loader 其实就是专门用于打包特定文件的处理程序
五、其他常用 loader
一般来说, 项目除了 js 文件外, 还有一些常用的文件, 如
- 图片文件
- css 文件
对于这些文件, webpack都不会打包, 需要我们安装对应的loader帮助webpack打包
1.打包图片文件
1> file-loader
作用:将文件复制到对应的路径, 并返回文件名
官网:https://www.webpackjs.com/loaders/file-loader/
i. 执行命令安装file-loader
npm install --save-dev file-loader
演示:
ii. 修改 webpack 配置, 添加一条规则
webpack.config.js
{
test: /\.(jpg|jpeg|png|svg)$/,
loader: 'file-loader'
}
演示:
iii. 测试
src目录下新增assets/images和assets/styles文件夹,随便找一张图片放入images里
在 app.vue 中引入图片, 重新打包, 会在 dist 目录下生成图片
演示:
此时文件名是一串hash值,如果希望保留原有的文件名, 可以使用占位符(placeholder)配置
webpack.config.js更新file-loader配置,重新打包
{
test: /\.(jpg|jpeg|png|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]'
}
}
演示:
2> url-loader
作用:功能类似于file-loader,但是在文件大小(单位 byte)低于指定的限制时,可以返回一个 DataURL。
好处:直接将小图片打包以 base64 打包在 js 中, 减少 Http 请求的次数, 提高访问效率
i 安装 url-loader
npm install -D url-loader
注意:由于 url-loader 依赖 file-loader, 因此安装 url-loader 需要先安装 file-loader, 一般来说同时安装更合适(npm install -D file-loader url-loader)
演示:
ii 配置
修改 webpack 配置, 添加一条规则
{
test: /\.(jpg|jpeg|png|svg)$/,
loader: 'url-loader',
options: {
name: '[name].[ext]',
limit: 2048
}
}
iii 测试
复制一张小的图片到 assets 目录下. 然后在 app.vue 中导入,最后重新打包
app.vue
演示:图片格式发生变化
2.打包 css 文件
webpack 通过 css-loader 和 style-loader 来打包 css 文件
- css-loader: 解决文件之间的依赖关系, 把所有的 css 文件打包成一个文件
- style-loader: 将 css-loader 打包完成后生成的文件挂载到页面的 head 标签的 style 中
1>安装
i 安装 css-loader
在安装 vue-loader 时, 我们已经安装了 css-loader. 可以通过查看package.json
确认
i i 安装 style-loader
npm install -D style-loader
演示:
2>配置loader
webpack.config.js
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
演示:
划重点:loader的书写顺序是有讲究的, 按照从右到左, 从下到上的顺序依次执行
3> 测试
i 在styles文件夹下新建样式文件index.css
* {
margin: 0;
padding: 0;
}
body {
background-color: red;
}
演示:
ii 在 App.vue 中导入 index.css
iii 执行命令npm run build
重新打包测试, 发现生效
4> 优化
实际项目中一般会把样式独立出来, 再通过 import 引入
3. 打包less文件
less-loader: css 预处理器
1> 安装
npm install --save-dev less-loader less
2> 配置
webpack.config.js
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
}
演示:
3> 测试
将所有css文件后缀名改为.less, 同时将app.vue中的引入文件同步修改为less后,重新打包
演示:(一切正常)
六、插件
插件:在某个时间点, 自动执行的处理程序(类似于 vue 的生命周期函数)
1. html-webpack-plugin 插件
作用:在打包结束时, 在 dist 目录下自动生成 index.html 文件, 并把打包好的 js 文件引入到 html 中
官网:https://www.webpackjs.com/plugins/html-webpack-plugin/
1> 安装
npm install -D html-webpack-plugin
2> 配置
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
……
new HtmlWebpackPlugin()
演示:
3> 打包测试
发现dist目录下多了index.html文件,打开此文件,发现报了个错,找不到#app的id
我们发现自动生成的 html 中没有 id 为 app 的 div 元素, 如何解决这个问题?
4> 指定模板
作用:指定生成 html 时, 以哪个文件为模板的
修改webpack.config.js
plugins: [
new HtmlWebpackPlugin({
template: './index.html'
})
],
演示:
2. clean-webpack-plugin插件
作用:每次打包前,先删除之前打的包
1> 安装
npm install -D clean-webpack-plugin
2> 配置
webpack.config.js
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
……
new CleanWebpackPlugin()
演示:
3> 打包测试
在dist目录下随便新建个啥文件,然后重新打包测试
我们发现新建的文件被干掉了
3. autoprefixer 插件
作用:打包后,自动添加样式的厂商前缀
注意:autoprefixer 插件是 postcss-loader 提供的一个插件, 如果要使用这个插件, 我们得先安装 postcss-loader
1> 安装
npm install -D postcss-loader autoprefixer
2> 配置
webpack.config.js更新配置
新建 postcss.config.js 配置文件
module.exports = {
plugins: [require('autoprefixer')]
};
演示:
3> 打包测试
在app.vue文件里新增一个css3的新特性样式,然后打包测试,发现报错
原因:autoprefixer下载时,没做版本控制,默认是仓库中最新的版本,但是最新版本和postcss-loader不兼容,怎么解决?
重新安装并指定版本
npm i postcss-loader autoprefixer@8.0.0
再次执行命令npm run build重新打包,正常
演示:
七、开发坏境
1.devServer
webpack-dev-server
为你提供了一个简单的 web 服务器,并且能够实时重新加载(live reloading),无需每次更新代码后,去手动打包刷新页面
1> 安装
npm install -D webpack-dev-server
2> 配置
webpack.config.js更新配置
devServer: {
// 指定服务器根目录
contentBase: './dist',
// 自动打开浏览器
open: true,
// 启用热模块替换
hot: true
}
package.json
"scripts": {
"start": "webpack-dev-server",
"build": "webpack"
}
演示:
3> 打包测试
启动服务测试
npm run start
发现报错
原因:查阅了其他人的解决,发现是因为
webpack-cli
的新版本对webpack-dev-server
版本的不兼容,怎么解决?👇
降低webpack-cli
的版本为 "^3.3.12"
npm i -D webpack-cli@3.3.12
再次启动服务npm run start, 发现启动正常,去代码里随便改个什么内容,浏览器自动刷新
4> 其他常见配置
- host: 服务器主机
- port: 端口
- open: 打开浏览器
- hot: 热模块替换(例如:页面上点击新增按钮,新加一个dom元素,然后去改代码,页面的新加的dom元素依旧保留;如果设置为false, 这个dom元素会消失,即和页面整个刷新时状态一致)
- proxy: 代理
2.SourceMap
SourceMap: (源代码映射) 建立打包后的文件和源代码所在行的映射
作用:在开发时快速定位到出错的源代码行
相关配置:https://www.webpackjs.com/configuration/devtool/👇
i webpack.config.js配置
devtool: 'eval-source-map'
演示:
ii 测试
随便造个错
测试通过:控制台可以直接定位到错误位置
八、生产环境
开发环境(development)和生产环境(production)的构建目标差异很大。在开发环境中,我们需要具有强大的、具有实时重新加载(live reloading)或热模块替换(hot module replacement)能力的 source map 和 localhost server。而在生产环境中,我们的目标则转向于关注更小的 bundle,更轻量的 source map,以及更优化的资源,以改善加载时间。由于要遵循逻辑分离,我们通常建议为每个环境编写彼此独立的 webpack 配置。
- webpack.dev.js: 用于开发环境
- webpack.prod.js: 用于生产环境
1.分别指定两个配置文件
webpack.dev.js
// 使用node的path模块
const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
// 打包入口
devtool: 'eval-source-map',
entry: './src/main.js',
devServer: {
// 指定服务器根目录
contentBase: './dist',
// 自动打开浏览器
open: true,
// 启用热模块替换
hot: true
},
// 打包出口
output: {
filename: 'app.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.(jpg|jpeg|png|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]'
}
},
{
test: /\.(jpg|jpeg|png|svg)$/,
loader: 'url-loader',
options: {
name: '[name].[ext]',
limit: 1077532
}
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader']
}
]
},
plugins: [
// 请确保引入这个插件!
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
template: './index.html'
}),
new CleanWebpackPlugin()
],
resolve: {
alias: {
'vue': 'vue/dist/vue.js'
}
}
}
webpack.prod.js
// 使用node的path模块
const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
mode: 'production',
// 打包入口
entry: './src/main.js',
// 打包出口
output: {
filename: 'app.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.(jpg|jpeg|png|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]'
}
},
{
test: /\.(jpg|jpeg|png|svg)$/,
loader: 'url-loader',
options: {
name: '[name].[ext]',
limit: 1077532
}
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader']
}
]
},
plugins: [
// 请确保引入这个插件!
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
template: './index.html'
}),
new CleanWebpackPlugin()
],
resolve: {
alias: {
'vue': 'vue/dist/vue.js'
}
}
}
package.json
"scripts": {
"start": "webpack-dev-server --config ./webpack.dev.js",
"build": "webpack --config ./webpack.prod.js"
}
2.提取公共部分
使用webpack-merge插件,请注意:
官方文档引入方式👇(会报错)
const merge = require('webpack-merge')
应该这样引入👇
const { merge } = require('webpack-merge')
1>安装webpack-merge插件
npm install -D webpack-merge
2>创建build目录
创建 3 个文件
- webpack.base.js: 公共配置
- webpack.dev.js: 开发环境配置
- webpack.prod.js: 生产环境配置
webpack.base.js
// 使用node的path模块
const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
// 打包入口
entry: './src/main.js',
// 打包出口
output: {
filename: 'app.js',
path: path.resolve(__dirname, '../dist')
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.(jpg|jpeg|png|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]'
}
},
{
test: /\.(jpg|jpeg|png|svg)$/,
loader: 'url-loader',
options: {
name: '[name].[ext]',
limit: 1077532
}
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader']
}
]
},
plugins: [
// 请确保引入这个插件!
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
template: './index.html'
}),
new CleanWebpackPlugin()
],
resolve: {
alias: {
'vue': 'vue/dist/vue.js'
}
}
}
webpack.dev.js
const { merge } = require('webpack-merge');
const baseConfig = require('./webpack.base.js');
const devConfig = {
mode: 'development',
devtool: 'eval-source-map',
devServer: {
// 指定服务器根目录
contentBase: './dist',
// 自动打开浏览器
open: true,
// 启用热模块替换
hot: true
}
};
module.exports = merge(baseConfig, devConfig);
webpack.prod.js
const { merge } = require('webpack-merge');
const baseConfig = require('./webpack.base.js');
const prodConfig = {
mode: 'production'
};
module.exports = merge(baseConfig, prodConfig);
3>更改package.json配置
package.json
"scripts": {
"start": "webpack-dev-server --config ./build/webpack.dev.js",
"build": "webpack --config ./build/webpack.prod.js"
}
九、解析ES6语法
官方文档:https://www.babeljs.cn/setup#installation
1.安装Babel
npm install --save-dev babel-loader @babel/core
2.配置Babel
webpack.base.js
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ['@babel/preset-env']
}
}
}
3.创建.babelrc
配置文件
npm install @babel/preset-env --save-dev
.babelrc文件
{
"presets": ["@babel/preset-env"]
}
4.测试
使用模板语法测试
重新打包,语法已被转换
十、引入element-ui组件库
1.安装element-ui
npm i element-ui -S
2.配置
官方文档写的很详细,请移步官方文档👇
官方文档:https://element.eleme.cn/#/zh-CN/component/quickstart
3.测试
i 引入table组件
i i 重新启动项目(报错)
原因:webpack不认识.woff结尾的文件,怎么解决? ——更新file-loader的配置
webpack.base.js
{
test: /\.(jpg|jpeg|png|svg|eot|svg|ttf|woff|woff2)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]'
}
}
iii 在重新启动项目,正常
十一、引入路由vue-router
官网: https://router.vuejs.org/zh/installation.html
1.安装
npm install vue-router
2.配置路由前准备两个页面
创建页面pages/pageOne/index.vue
<template>
<div>
我是pageOne页面
</div>
</template>
<script>
export default {
data() {
return {
}
},
created() {
console.log('111111111')
},
methods: {
}
}
</script>
<style lang="less" scoped>
</style>
创建页面pages/pageTwo/index.vue
<template>
<div>
我是pageTwo页面
</div>
</template>
<script>
export default {
data() {
return {
}
},
created() {
console.log('2222222222')
},
methods: {
}
}
</script>
<style lang="less" scoped>
</style>
演示:
3.配置路由
创建路由文件router/index.js
import Vue from 'vue';
import VueRouter from 'vue-router';
import PageOne from '@/pages/pageOne/index.vue'
import PageTwo from '@/pages/pageTwo/index.vue'
Vue.use(VueRouter);
export default new VueRouter({
routes: [
{
path: '/pageOne',
name: 'pageOne',
component: PageOne
},
{
path: '/pageTwo',
name: 'pageTwo',
component: PageTwo
}
]
})
在main.js里,通过 router 配置参数注入路由
4.测试
在app.vue文件做下更改,用来点击切换路由
<template>
<div>
<div>我是首页</div>
<br/>
<el-button @click="onClick(1)" :disabled="isDisabled" type="primary">Go to pageOne</el-button>
<br/>
<br/>
<el-button @click="onClick(2)" :disabled="!isDisabled" type="primary">Go to pageTwo</el-button>
<br/>
<br/>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
isDisabled: false
}
},
methods: {
onClick(type) {
this.isDisabled = !this.isDisabled;
this.$router.push({
name: type === 1 ? 'pageOne' : 'pageTwo'
})
}
}
}
</script>
<style lang="less" scoped>
</style>
演示:(顺利)
十二、最终项目效果
加个左边菜单栏,实现路由和左边菜单栏激活状态的交互,这块就不详细说了,感兴趣的小伙伴,可以进我的github去查看👇
github地址:https://github.com/wanglingzhi1016/vue-webpack-elementUi