主要內(nèi)容
一. 初始化工程項(xiàng)目
二.添加熱部署
三.添加vue套餐
四.添加換膚功能
五.添加項(xiàng)目端口提示
六.添加axios并二次封裝
七.添加moke.js模擬后端數(shù)據(jù)
八.設(shè)置環(huán)境變量
一. 初始化工程項(xiàng)目
生成基礎(chǔ)項(xiàng)目
npm init -y
創(chuàng)建 src辱挥、dist目錄 , src/main.js蝗砾、index.html、webpack.config.js文件
目錄如下
project
├── dist
├── src
│ └── main.js
├── index.html
├── package.json
└── webpack.config.js
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>webpack</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
main.js
var content = document.createElement("div");
content.textContent = "Hello webpack!";
document.querySelector("#app").appendChild(content);
引入webpack webpack-cli html-webpack-plugin三個(gè)包
npm i webpack webpack-cli html-webpack-plugin -D
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: __dirname + "/src/main.js", // 打包入口
output: { // 出口文件
path: __dirname + "/dist", // 打包后存放的地方
filename: "bundle.js" // 打包后的文件命名
},
plugins: [
new HtmlWebpackPlugin({
title: 'webpack',
minify: { // 壓縮HTML文件
removeComments: true, // 移除HTML中的注釋
collapseWhitespace: true, // 刪除空白符與換行符
minifyCSS: true// 壓縮內(nèi)聯(lián)css
},
filename: 'index.html', // 輸出的模板名字
template: 'index.html' // 模板路徑
})
]
}
package.json scripts下面添加一行命令
"build": "webpack --mode production"
執(zhí)行下面代碼
npm run build
可以看到dist目錄多出了兩個(gè)打包文件洞就,一個(gè)是index.html,一個(gè)是bundle.js
瀏覽器打開(kāi)index.html
可以看到頁(yè)面出現(xiàn)了Hello webpack! OK到這里已經(jīng)完成了項(xiàng)目的簡(jiǎn)單打包了。
教練我看到別人的項(xiàng)目可以實(shí)時(shí)刷新掀淘,我也想學(xué)旬蟋,OK,熱部署安排一下革娄。
二.添加熱部署
安裝熱部署依賴
npm i -D webpack-dev-server
webpack.config.js文件添加devServer的配置
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: __dirname + "/src/main.js", // 打包入口
output: { // 出口文件
path: __dirname + "/dist", // 打包后存放的地方
filename: "bundle.js" // 打包后的文件命名
},
devServer: {
contentBase: "./dist", // 本地服務(wù)器所加載的頁(yè)面所在的目錄
historyApiFallback: true, // 找不到界面默認(rèn)返回首頁(yè)
inline: true, // 實(shí)時(shí)刷新
port: 8888
},
plugins: [
new HtmlWebpackPlugin({
title: 'webpack',
minify: { // 壓縮HTML文件
removeComments: true, // 移除HTML中的注釋
collapseWhitespace: true, // 刪除空白符與換行符
minifyCSS: true// 壓縮內(nèi)聯(lián)css
},
filename: 'index.html', // 輸出的模板名字
template: 'index.html' // 模板路徑
})
]
}
理所當(dāng)然package.json也是要相對(duì)應(yīng)修改
"dev": "webpack-dev-server --open"
運(yùn)行項(xiàng)目
npm run dev
可以看到webpack運(yùn)行后自動(dòng)打開(kāi)了默認(rèn)瀏覽器并在頁(yè)面上輸出了Hello webpack!
修改下main.js內(nèi)容
var content = document.createElement("div");
content.textContent = "Hello webpack! Webpack牛批咖为!";
document.querySelector("#app").appendChild(content);
保存修改后可以發(fā)現(xiàn)瀏覽器的內(nèi)容也隨著自動(dòng)刷新了,到這里webpack熱加載就完成了稠腊。
三.添加vue套餐
安裝babel,vue套餐依賴躁染,css依賴 @注意這里的babel-loader建議安裝7.x版本 頭鐵的可以不指定版本安裝最新版
-S --save 運(yùn)行時(shí)依賴 dependencies
-D --save-dev 開(kāi)發(fā)時(shí)依賴 devDependencies
此外還有peerDependencies、bundleDependencies架忌、optionalDependencies
前兩項(xiàng)其實(shí)已經(jīng)足夠日常使用了吞彤,后三項(xiàng)是作為npm包的發(fā)布者需要考慮使用的,有興趣可以查閱原文章以及npm的文檔
npm i -S vue vue-router
npm i -D babel-core babel-loader@7.1.1 vue-loader vue-loader vue-template-compiler css-loader
項(xiàng)目中添加src/views叹放、src/router目錄饰恕, src/views/index.vue src/router/index.js、src/App.vue 文件
目錄如下,帶有+號(hào)的就是新增的
project
├── index.html
├── list.md
├── package.json
├── dist
│ ├── bundle.js
│ └── index.html
├── src
│ ├── App.vue ++++++++++++++++
│ ├── main.js
│ ├── router ++++++++++++++++
│ │ └── index.js ++++++++++++++++
│ └── views ++++++++++++++++
│ └── index.vue ++++++++++++++++
└── webpack.config.js
views/index.vue
<template>
<div>vue webpack</div>
</template>
<script>
export default {
name: 'Home',
data () {
return {
}
}
}
</script>
<style>
</style>
router/index.js
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'home',
component: (resolve) => require(['../views/index.vue'], resolve),
}
],
mode: 'history'
})
App.vue
<template>
<div id="app">
<router-view/>
</div>
</template>
<script>
export default {
name: 'App',
components: {
},
data () {
return {
}
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
width: 100%;
min-width: 1200px;
height: 100%;
top: 0;
left: 0;
}
</style>
main.js
import Vue from "vue";
import App from "./App.vue";
import router from './router'
new Vue({
el: "#app",
router,
components: {
App,
},
template: "<App/>"
});
webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
// webpack4以上要添加VueLoaderPlugin
const VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
entry: __dirname + "/src/main.js", // 打包入口
output: { // 出口文件
path: __dirname + "/dist", // 打包后存放的地方
filename: "bundle.js", // 打包后的文件命名
publicPath: '/'
},
devServer: {
contentBase: "./dist", // 本地服務(wù)器所加載的頁(yè)面所在的目錄
historyApiFallback: false, // 找不到界面默認(rèn)返回首頁(yè)
inline: true, // 實(shí)時(shí)刷新
port: 8888
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js' //重定向代理
}
},
module: {
rules: [
{
test: /\.css$/,
use:'css-loader',
include: path.resolve(__dirname + '/src/'),
exclude: /node_modules/
},
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.js$/,
loader: 'babel-loader',
include: /node_modules/
}
]
},
plugins: [
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
title: 'webpack',
minify: { // 壓縮HTML文件
removeComments: true, // 移除HTML中的注釋
collapseWhitespace: true, // 刪除空白符與換行符
minifyCSS: true// 壓縮內(nèi)聯(lián)css
},
filename: 'index.html', // 輸出的模板名字
template: 'index.html' // 模板路徑
})
]
}
運(yùn)行項(xiàng)目
npm run dev
修改下index.vue里面的內(nèi)容井仰,把vue webpack改成hello webpack
下面添加vuex
cnpm i -S vuex
src目錄下添加store埋嵌、store/module目錄, 添加store/index.js、store/store俱恶、store/module/main.js
目錄如下
project
├── index.html
├── list.md
├── package.json
├── dist
│ ├── bundle.js
│ └── index.html
├── src
│ ├── App.vue
│ ├── main.js
│ ├── router
│ │ └── index.js
│ ├── store ++++++++++++++++++++++
│ │ ├── getters.js ++++++++++++++++++++++
│ │ ├── index.js ++++++++++++++++++++++
│ │ └── modules ++++++++++++++++++++++
│ │ └── main.js ++++++++++++++++++++++
│ └── views
│ └── index.vue
└── webpack.config.js
src/main.js
import Vue from "vue";
import App from "./App.vue";
import router from './router'
import store from './store'
new Vue({
el: "#app",
router,
store,
components: {
App,
},
template: "<App/>"
});
store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
Vue.use(Vuex)
const state = {
}
const mutations = {
}
const actions = {
}
//獲取modules下面的文件數(shù)組
const modulesFiles = require.context('./modules', false, /\.js$/)
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = modulesFiles(modulePath)
modules[moduleName] = value.default
return modules
}, {})
const store = new Vuex.Store({
modules,
getters,
mutations,
actions,
state
})
export default store
getters.js
const getters = {
test: state => state.main.test
}
export default getters
store/modules/main.js
const state = {
text: 'webpack 牛批雹嗦!'
}
const mutations = {
}
const actions = {}
export default {
namespaced: true,
state,
mutations,
actions
}
views/index.vue
<template>
<div>{{text}}</div>
</template>
<script>
import { mapGetters } from "vuex"
export default {
name: 'Home',
data () {
return {
}
},
computed: {
...mapGetters(["text"])
},
mounted() {
console.log(this, "11")
}
}
</script>
<style>
</style>
運(yùn)行項(xiàng)目
npm run dev
頁(yè)面上出現(xiàn)webpack 牛批!就OK了
四.添加換膚功能
添加全局sass變量
npm i -D style-loader node-sass sass-loader sass-resources-loader postcss-loader autoprefixer
src下創(chuàng)建assets合是、assets/css目錄, assets/css下創(chuàng)建index.scss了罪、mixin.scss、reset.css聪全、theme.scss泊藕、common.scss、flex.scss
目錄如下
project
├── index.html
├── package.json
├── dist
│ ├── bundle.js
│ └── index.html
├── src
│ ├── App.vue
│ ├── assets ++++++++++++++++++++
│ │ └── css ++++++++++++++++++++
│ │ ├── common.scss ++++++++++++++++++++
│ │ ├── index.scss ++++++++++++++++++++
│ │ ├── mixin.scss ++++++++++++++++++++
│ │ ├── reset.css ++++++++++++++++++++
│ │ ├── flex.css ++++++++++++++++++++
│ │ └── theme.scss ++++++++++++++++++++
│ ├── main.js
│ ├── router
│ │ └── index.js
│ ├── store
│ │ ├── getters.js
│ │ ├── index.js
│ │ └── modules
│ │ └── main.js
│ └── views
│ └── index.vue
└── webpack.config.js
theme.scss
$font-color-theme1: blue;
$bg-theme1: blue;
$font-color-theme2: red;
$bg-theme2: red;
$font-color-theme3: yellow;
$bg-theme3: yellow;
reset.css
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
html {
height: 100%;
}
body {
line-height: 1;
height: 100%;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
a{
text-decoration: none;
color: #000;
}
a:active{
text-decoration: none;
color: #000;
}
.clearfixed{
clear: both;
}
.clearfixed::after, .clearfixed::before {
content: "";
clear: both;
display: block;
height: 0;
width: 0
}
index.scss
@import "./common.scss";
@import "./flex.scss";
@import "./reset.css";
mixin.scss
@mixin font_color {
color: $primary-theme1;
[data-theme="theme1"] & {
color: $primary-theme1;
}
[data-theme="theme2"] & {
color: $primary-theme2;
}
[data-theme="theme3"] & {
color: $primary-theme3;
}
}
@mixin primary_color {
color: $primary-theme1;
[data-theme="theme1"] & {
color: $primary-theme1;
}
[data-theme="theme2"] & {
color: $primary-theme2;
}
[data-theme="theme3"] & {
color: $primary-theme3;
}
}
@mixin bg_color {
background: $primary-theme1;
[data-theme="theme1"] & {
background: $primary-theme1;
}
[data-theme="theme2"] & {
background: $primary-theme2;
}
[data-theme="theme3"] & {
background: $primary-theme3;
}
}
@mixin solid_color {
border: 1px solid $primary-theme1;
[data-theme="theme1"] & {
border: 1px solid $primary-theme1;
}
[data-theme="theme2"] & {
border: 1px solid $primary-theme2;
}
[data-theme="theme3"] & {
border: 1px solid $primary-theme3;
}
}
// 單行省略號(hào)
@mixin Ellipsis{
overflow: hidden;text-overflow: ellipsis;white-space: nowrap;
}
// 多行省略號(hào)
@mixin EllipsisMore($height, $lineHeight, $num){
width:100%;
min-height: 20px;
max-height: $height;
line-height: $lineHeight;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: $num;
-webkit-box-orient: vertical;
}
// flex居中
@mixin flexCenter{
display: flex;
justify-content: center;
align-items: center;
}
// flex居中
@mixin flexCenter{
display: flex;
justify-content: center;
align-items: center;
}
common.scss
.clear {
clear: both;
}
.cursor {
cursor: pointer;
}
.font12 {
font-size: 12px;
}
.font14 {
font-size: 14px;
}
.font16 {
font-size: 16px;
}
.font18 {
font-size: 18px;
}
.font20 {
font-size: 20px;
}
.font24 {
font-size: 24px;
}
.font30 {
font-size: 30px;
}
.font36 {
font-size: 36px;
}
.font46 {
font-size: 46px;
}
.weight {
font-weight: bold;
}
.clear {
clear: bold;
}
.clearfloat:after {
display: block;
clear: both;
content: "";
visibility: hidden;
height: 0;
}
.clearfloat {
zoom: 1;
}
[v-cloak] {
display: none;
}
.el-select .el-input .el-select__caret {
-ms-transition: transform .3s;
-ms-transform: rotateZ(180deg);
}
.font-text {
font-size: 14px;
color: #999;
}
.font-title {
color: #333;
font-size: 18px;
}
.hot {
color: #b71ed7;
font-weight: 500;
font-style: inherit;
}
.red {
color: #e8001c;
font-weight: 500;
font-style: inherit;
}
.hoverColor1 {
cursor: pointer;
&:hover {
color: #ef5924 !important;
}
}
.hoverColor2 {
cursor: pointer;
&:hover {
background: #f1f1f1 !important;
}
}
flex.scss
.flex {
display: flex;
}
.flex1 {
flex: 1;
}
.flex2 {
flex: 2;
}
.flex3 {
flex: 3;
}
.flex4 {
flex: 4;
}
.jcenter {
justify-content: center;
}
.column {
flex-direction: column;
}
.row {
flex-direction: row;
}
.aitem {
align-items: center;
}
.clearbox {
box-sizing: border-box;
}
.jaround {
justify-content: space-around;
}
.jbetween {
justify-content: space-between;
}
webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
// webpack4以上要添加VueLoaderPlugin
const VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
entry: __dirname + "/src/main.js", // 打包入口
output: { // 出口文件
path: __dirname + "/dist", // 打包后存放的地方
filename: "bundle.js", // 打包后的文件命名
publicPath: '/'
},
devServer: {
contentBase: "./dist", // 本地服務(wù)器所加載的頁(yè)面所在的目錄
historyApiFallback: false, // 找不到界面默認(rèn)返回首頁(yè)
inline: true, // 實(shí)時(shí)刷新
port: 8888
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js' , // 重定向代理
+ "@": path.resolve(__dirname + "/src/") // 設(shè)置模塊基路徑
}
},
module: {
rules: [
{
test: /\.css$/,
use:'css-loader',
include: path.resolve(__dirname + '/src/'),
exclude: /node_modules/
},
+ {
+ test: /\.scss$/,
+ use: [
+ 'style-loader', // 將 JS 字符串生成為 style 節(jié)點(diǎn)
+ 'css-loader', // 將 CSS 轉(zhuǎn)化成 CommonJS 模塊
+ 'sass-loader', // 將 Sass 編譯成 CSS
+ {
+ loader: "postcss-loader",//添加瀏覽器兼容后綴
+ options: {
+ plugins: [
+ require("autoprefixer")(),
+ ]
+ }
+ },
+ {
+ loader: 'sass-resources-loader', // 絕對(duì)路徑引入主題色文件
+ options: {
+ resources: [
+ path.resolve(__dirname + "/src/assets/css/theme.scss"),
+ path.resolve(__dirname + "/src/assets/css/mixin.scss")
+ ]
+ },
+ },
+ ]
+ },
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.js$/,
loader: 'babel-loader',
include: /node_modules/
}
]
},
plugins: [
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
title: 'webpack',
minify: { // 壓縮HTML文件
removeComments: true, // 移除HTML中的注釋
collapseWhitespace: true, // 刪除空白符與換行符
minifyCSS: true// 壓縮內(nèi)聯(lián)css
},
filename: 'index.html', // 輸出的模板名字
template: 'index.html' // 模板路徑
})
]
}
到這里基本是完成了难礼,不過(guò)是scss全局變量完成娃圆,實(shí)行換膚還差一點(diǎn)點(diǎn)
src下新建utils文件夾,新建utils/index.js文件
目錄如下
project
├── index.html
├── package.json
├── dist
│ ├── bundle.js
│ └── index.html
├── src
│ ├── App.vue
│ ├── assets
│ │ └── css
│ │ ├── common.scss
│ │ ├── index.scss
│ │ ├── mixin.scss
│ │ ├── reset.css
│ │ └── theme.scss
│ ├── main.js
│ ├── router
│ │ └── index.js
│ ├── store
│ │ ├── getters.js
│ │ ├── index.js
│ │ └── modules
│ │ └── main.js
│ ├── utils ++++++++++
│ │ └── index.js ++++++++++
│ └── views
│ └── index.vue
└── webpack.config.js
src/main.js修改
import Vue from "vue";
import App from "./App.vue";
import router from './router'
import store from './store'
import "@/assets/css/index.scss";
if(localStorage.getItem("WINDOWS_THEME")) {
window.document.documentElement.setAttribute('data-theme', localStorage.getItem("WINDOWS_THEME"))
}
new Vue({
el: "#app",
router,
store,
components: {
App,
},
template: "<App/>"
});
views/index.vue修改
<template>
<div class="index-wrapper">
<header class="index-header flex">
<div class="theme theme1"></div>
<div class="theme theme2"></div>
<div class="theme theme3"></div>
</header>
<div class="content">
<div class="primary jcenter aitem flex">主題色</div>
<div class="bth-box">
<div class="list cursor theme1" @click="changeTheme('theme1')">改變主題色1</div>
<div class="list cursor theme2" @click="changeTheme('theme2')">改變主題色2</div>
<div class="list cursor theme3" @click="changeTheme('theme3')">改變主題色3</div>
</div>
</div>
</div>
</template>
<script>
import { mapGetters } from "vuex"
import { setTheme } from "@/utils"
export default {
name: 'Home',
data () {
return {
}
},
computed: {
...mapGetters(["text"])
},
methods: {
changeTheme(type) {
setTheme(type)
}
},
mounted() {
console.log(this, "11")
}
}
</script>
<style lang="scss" scoped>
.index-wrapper {
.theme {
width: 100px;
height: 100px;
}
.theme1 {
background: $primary-theme1;
}
.theme2 {
background: $primary-theme2;
}
.theme3 {
background: $primary-theme3;
}
.content {
margin: 50px;
display: flex;
.primary {
width: 100px;
height: 100px;
@include bg_color;
color: #fff;
}
.bth-box {
margin-left: 20px;
.list {
width: 100px;
height: 30px;
line-height: 30px;
border: 1px solid #ddd;
border-radius: 2px;
margin-bottom: 10px;
text-align: center;
color: #fff;
&.theme1 {
background: blue;
}
&.theme2 {
background: red;
}
&.theme3 {
background: yellow;
}
}
}
}
}
</style>
現(xiàn)在頁(yè)面是這樣的
點(diǎn)擊改變主題色按鈕可以切換了蛾茉,換膚功能完成讼呢。
五.添加項(xiàng)目端口提示
現(xiàn)在項(xiàng)目運(yùn)行完成是這樣的
別人的項(xiàng)目是這樣的
好整潔,教練我想學(xué)臀稚,OK
添加控制臺(tái)提示依賴包吝岭,順便把靜態(tài)資源打包插件安裝上三痰,還有文件清除插件
npm i -D friendly-errors-webpack-plugin copy-webpack-plugin clean-webpack-plugin
新建config吧寺、static文件夾窜管,config/index.js,statuc/test.txt文件
目錄如下
project
├── index.html
├── package.json
├── dist
│ ├── bundle.js
│ └── index.html
├── config +++++++
│ └── index.js +++++++
├── static +++++++
│ └── test.txt +++++++
...
config/index.js
module.exports = {
devServer: {
contentBase: "../dist", // 本地服務(wù)器所加載的頁(yè)面所在的目錄
publicPath: '/', // 公共路徑 打包后資源可以訪問(wèn)的路徑
historyApiFallback: true, // 找不到界面默認(rèn)返回首頁(yè)
inline: true, //實(shí)時(shí)刷新
host: '0.0.0.0',
port: 8888,
open: false
},
onErrors: ()=> {
const notifier = require('node-notifier')
return (severity, errors) => {
if (severity !== 'error') return
const error = errors[0]
const filename = error.file && error.file.split('!').pop()
notifier.notify({
title: packageConfig.name,
message: severity + ': ' + error.name,
subtitle: filename || '',
icon: path.join(__dirname, 'logo.png')
})
}
}
}
webpack.config.js修改(注意) // copy-webpack-plugin v5.12以上的版本要添加patterns
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const copyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// webpack4以上要添加VueLoaderPlugin
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const config = require('./config')
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
module.exports = {
entry: __dirname + "/src/main.js", // 打包入口
output: { // 出口文件
path: __dirname + "/dist", // 打包后存放的地方
filename: "bundle.js", // 打包后的文件命名
publicPath: '/'
},
devServer: config.devServer,
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js', // 重定向代理
"@": path.resolve(__dirname + "/src/") // 設(shè)置模塊基路徑
}
},
module: {
rules: [
{
test: /\.css$/,
use:'css-loader',
include: path.resolve(__dirname + '/src/'),
exclude: /node_modules/
},
{
test: /\.scss$/,
use: [
'style-loader', // 將 JS 字符串生成為 style 節(jié)點(diǎn)
'css-loader', // 將 CSS 轉(zhuǎn)化成 CommonJS 模塊
'sass-loader', // 將 Sass 編譯成 CSS
{
loader: "postcss-loader",//添加瀏覽器兼容后綴
options: {
plugins: [
require("autoprefixer")(),
]
}
},
{
loader: 'sass-resources-loader', // 絕對(duì)路徑引入主題色文件
options: {
resources: [
path.resolve(__dirname + "/src/assets/css/theme.scss"),
path.resolve(__dirname + "/src/assets/css/mixin.scss")
]
},
},
]
},
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.js$/,
loader: 'babel-loader',
include: /node_modules/
}
]
},
plugins: [
// v5.12以上的版本要添加patterns
new copyWebpackPlugin({
patterns: [
{
from:path.resolve(__dirname+'/static'),// 打包的靜態(tài)資源目錄地址 不經(jīng)過(guò) webpack,需要通過(guò)絕對(duì)路徑來(lái)引用
to:'static' // 打包到dist下面的static
}
]
}),
new VueLoaderPlugin(),
new CleanWebpackPlugin(), // 每次打包都先刪除打包目錄
new FriendlyErrorsWebpackPlugin({
compilationSuccessInfo: {
messages: [`Your application is running here: http://${config.devServer.host}:${config.devServer.port}`],
},
onErrors: true ? config.devServer.onErrors: undefined // 是否開(kāi)啟錯(cuò)誤捕獲
}),
new HtmlWebpackPlugin({
title: 'webpack',
minify: { // 壓縮HTML文件
removeComments: true, // 移除HTML中的注釋
collapseWhitespace: true, // 刪除空白符與換行符
minifyCSS: true// 壓縮內(nèi)聯(lián)css
},
filename: 'index.html', // 輸出的模板名字
template: 'index.html' // 模板路徑
})
],
stats:"none"
}
啟動(dòng)項(xiàng)目
npm run dev
灰常好稚机,到這里添加控制臺(tái)提示就結(jié)束了幕帆,當(dāng)然FriendlyErrorsWebpackPlugin不僅僅只有啟動(dòng)項(xiàng)目的提示信息,還能識(shí)別某些類別的webpack錯(cuò)誤赖条,并清理失乾,聚合和優(yōu)先級(jí),以提供更好的開(kāi)發(fā)人員體驗(yàn)纬乍。
六.添加請(qǐng)求方法axios并二次封裝
安裝axios
npm i -S axios
src下添加添加api碱茁、data文件夾,添加api/index.js仿贬、api/axiosExpand.js纽竣、data/index.js、test.js目錄如下
project
├── index.html
├── package.json
├── src
│ ├── App.vue
│ ├── api ++++++++
│ │ ├── index.js ++++++++
│ │ └── axiosExpand.js ++++++++
│ ├── data ++++++++
│ │ ├── index.js ++++++++
│ │ └── test.js ++++++++
...
api/index.js
/* eslint-disable */
/**
* @author jie
* date 2019/3/26
* data 文件夾下面放不同模塊的接口
* method: 'get' 'delete' 'post' 'put'
* value: true 接口有value時(shí) 參數(shù)以鍵值對(duì)的值拼接到鏈接后面 xxx.com/getUser?name=哈哈&id=2
* linkId: true 接口有l(wèi)inkId時(shí) id帶在鏈接后面 類似 xxx.com/getUser/10
* random: true 接口有random時(shí) 添加隨機(jī)數(shù)防止接口緩存
* headerToken 是否使用token 目前所有接口都默認(rèn)使用
* bodyParam: true 請(qǐng)求如果需要一部分參數(shù)帶頭部,一部分參數(shù)body需要在請(qǐng)求的時(shí)候把參數(shù)獨(dú)立放到參數(shù)里面處理
*
*/
import Axios from 'axios'
import apiData from '../data'
import qs from 'querystring'
import {
catchAjaxResolve,
catchAjaxReject
} from './axiosExpand'
// 設(shè)置默認(rèn)值
Axios.defaults.timeout = 30000;
Axios.defaults.baseURL = "/";
Axios.defaults.headers = {
'Content-Type': 'application/json'
};
// 數(shù)據(jù)請(qǐng)求之前
Axios.interceptors.request.use((config) => {
return config
}, error => {
promise.reject(error)
})
// 提交成功并返回?cái)?shù)據(jù)時(shí)
Axios.interceptors.response.use(response => {
return catchAjaxResolve(response)
},
error => {
return catchAjaxReject(error)
}
)
function formatAxios(url, data, params, requestType) {
if (params.linkId) {
url += '/' + data.id
data = null
}
if (params.random) {
if (Object.keys(data)) {
url += '&time=' + Math.random()
} else {
url += '?time=' + Math.random()
}
}
if (params.value || requestType === 'get' || requestType === 'delete') {
url += '?' + qs.stringify(data)
data = null
}
if (data && data.bodyParam) {
const bodyParam = JSON.parse(JSON.stringify(data.bodyParam));
delete data.bodyParam
url += '?' + qs.stringify(data)
data = bodyParam
}
return Axios[requestType](url, data)
}
function Http(name, data = {}) {
if (Boolean(apiData[name].headers)) {
for (let keys in apiData[name].headers) {
if (apiData[name].headers.hasownproperty(keys)) {
Axios.defaults.headers[keys] = apiData[name].headers[keys]
}
}
}
return formatAxios(apiData[name].url, data, apiData[name], apiData[name].method)
}
export default Http
api/axiosExpand.js
// 這里用的是elementUI 彈框提示
// import { Message } from 'element-ui'
// 處理ajax頻繁拋出錯(cuò)誤 3秒內(nèi)只顯示一個(gè)錯(cuò)誤信息
export function sendOut(msg) {
if (!localStorage.ajax_oldTime) {
localStorage.ajax_oldTime = new Date()
// Message.error(msg)
}
if (((new Date() - new Date(localStorage.ajax_oldTime)) / 1000) > 3) {
// Message.error(msg)
localStorage.ajax_oldTime = new Date()
}
}
// 處理ajax請(qǐng)求成功異常
export function catchAjaxResolve(response) {
switch (response.data.code) {
case 200:
// IE9時(shí)response.data是undefined茧泪,因此需要使用response.request.responseText(Stringify后的字符串)
let data = null;
if (response.data == undefined) {
data = response.request.responseText
} else {
data = response.data
}
return Promise.resolve(data)
case 401:
// 彈出錯(cuò)誤信息 看情況處理
return false
case 500:
// 彈出錯(cuò)誤信息 看情況處理
return false
default:
return false
}
}
// 處理ajax請(qǐng)求錯(cuò)誤
export function catchAjaxReject(error) {
switch (error.response.status) {
case 401:
sendOut('登陸失效')
break
case 404:
sendOut('網(wǎng)絡(luò)請(qǐng)求不存在')
break
case 504:
sendOut('網(wǎng)頁(yè)請(qǐng)求超時(shí)')
break
// 其他錯(cuò)誤蜓氨,直接拋出錯(cuò)誤提示
default:
sendOut('網(wǎng)絡(luò)異常')
}
return Promise.reject(error.response)
}
main.js添加
import Axios from '@/api'
Vue.prototype.$Axios = Axios
data/index
/**
* @description ajax接口聚合
* @author jie
* @date 2019/8/16
* @returns VOID
* @version 1.0
*/
const files = require.context('./', false, /[^index.js]*\.js$/)
let modules = {}
files.keys().forEach((key) => {
modules = {...modules, ...files(key).default || files(key)}
})
export default modules;
data/test.js, 這里推薦以模塊分類命名,然后里面的接口xxx模塊名_xxx功能
/**
* @description 測(cè)試模塊相關(guān)接口
* @author jie
* @date 2019/8/16
* @returns VOID
* @version 1.0
*/
export default {
// 添加
test_add: {
url: '/api/save',
interFaceType: 'api',
method: 'post',
bodyParam: true
},
// 刪除
test_delete: {
url: '/api/delete',
interFaceType: 'api',
method: 'delete'
},
// 查詢
test_getList: {
url: '/api/select',
interFaceType: 'api',
method: 'get',
linkId: true
},
// 修改
test_updateList: {
url: '/api/update',
interFaceType: 'api',
method: 'put'
},
}
七.添加moke.js模擬后端數(shù)據(jù)
添加依賴
npm install -D mockjs
添加mock文件夾队伟,mock/index.js穴吹、mock/test.js文件目錄如下
project
├── mock +++++++++
│ ├── index.js +++++++++
│ └── test.js +++++++++
...
mack/index.js
@注 Mock.mock( rurl, rtype, template ) rurl可以是字符串或者正則 為了避免get請(qǐng)求帶有參數(shù)的情況下匹配失效盡量使用正則來(lái)匹配
傳送門(mén):MockJS
/**
* @description moke接口聚合
* @author jie
* @date 2019/8/16
* @returns VOID
* @version 1.0
*/
// 引入mockjs
const Mock = require('mockjs');
const files = require.context('./', false, /[^index.js]*\.js$/)
let modules = {}
files.keys().forEach((key) => {
modules = {...modules, ...files(key).default || files(key)}
})
for(let key in modules) {
if(modules.hasOwnProperty(key)) {
let obj = modules[key]
obj.isMock && Mock.mock(new RegExp(obj.url + ".*"), obj.method, obj.fun); // Mock.mock( rurl, rtype, template ) rurl可以是字符串或者正則 為了避免get請(qǐng)求帶有參數(shù)的情況下匹配失效盡量使用正則來(lái)匹配
}
export default modules;
mock/test.js, url: 需要模擬的接口路徑,method: 對(duì)應(yīng)的請(qǐng)求方法嗜侮,fun模擬數(shù)據(jù)的函數(shù)港令,詳細(xì)的可以去看mock的文檔或者相關(guān)教程
isMock 一鍵開(kāi)關(guān),是否使用mock全在你一念之間
/**
* @description mock測(cè)試模塊相關(guān)接口
* @author jie
* @date 2019/8/16
* @param { isMock } 是否啟用mock數(shù)據(jù)
* @returns VOID
* @version 1.0
*/
// 引入mockjs
const Mock = require('mockjs');
// 獲取 mock.Random 對(duì)象
const Random = Mock.Random;
export default {
// 查詢
test_getList: {
url: '/api/select',
method: 'get',
isMock: true,
fun: function() {
let testData = []
for (let i = 0; i < 100; i++) {
let newArticleObject = {
title: Random.csentence(5, 30), // 隨機(jī)生成5-30的中文句子
name: Random.cname(), // 隨機(jī)生成一個(gè)常見(jiàn)的中文姓名
date: Random.date() // 指示生成的日期字符串的格式,默認(rèn)為yyyy-MM-dd
}
testData.push(newArticleObject)
}
console.log(testData)
return {
code: 200,
data: testData
};
}
},
// 添加
test_add: {
url: '/api/save',
method: 'get',
isMock: false,
fun: function() {
let testData = []
for (let i = 0; i < 100; i++) {
let newArticleObject = {
title: Random.csentence(5, 30), // 隨機(jī)生成5-30的中文句子
name: Random.cname(), // 隨機(jī)生成一個(gè)常見(jiàn)的中文姓名
date: Random.date() // 指示生成的日期字符串的格式,默認(rèn)為yyyy-MM-dd
}
testData.push(newArticleObject)
}
console.log(testData)
return {
code: 200,
data: testData
};
}
},
}
src/main.js
// 引入mockjs
require('./mock')
views/index
methods: {
changeTheme(type) {
setTheme(type)
},
async addList() {
let json = {
id: 10
}
const res = await this.$Axios("test_add", json)
if(res) {
console.log(res, "11")
}
},
async deleteList() {
let json = {
name: "vue",
id: 10
}
const res = await this.$Axios("test_delete", json)
if(res) {
console.log(res)
}
},
async getList() {
let json = {
name: "vue",
id: 10
}
const res = await this.$Axios("test_getList", json)
if(res) {
console.log(res, "111")
}
},
async updateList() {
let json = {
name: "vue",
id: 10
}
const res = await this.$Axios("test_updateList", json)
if(res) {
console.log(res)
}
},
},
mounted() {
this.addList()
this.deleteList()
this.getList()
this.updateList()
}
控制臺(tái)打印
到這里mock數(shù)據(jù)就完成了
八.設(shè)置環(huán)境變量
安裝依賴
npm i -S dotenv
根目錄添加環(huán)境變量.env.development锈颗,.env.beta缠借,.env.production文件
.env.development
ENV = 'development'
INTERFACE_NAME = 'test.com1'
.env.beta
INTERFACE_NAME = 'test.com2'
.env.production
INTERFACE_PATH = 'test.com3'
config/index.js修改
const fs = require('fs')
// 引入環(huán)境變量依賴
const dotenv = require('dotenv')
module.exports = {
mode: "development",
devServer: {
contentBase: "./dist", // 本地服務(wù)器所加載的頁(yè)面所在的目錄
publicPath: '/', // 公共路徑 打包后資源可以訪問(wèn)的路徑
historyApiFallback: true, // 找不到界面默認(rèn)返回首頁(yè)
disableHostCheck: true, // 檢查主機(jī)host 開(kāi)發(fā)環(huán)境下可建議禁止
inline: true, //實(shí)時(shí)刷新
host: 'localhost',
port: 8888,
open: false
},
hiddleEnv: (env) => {
let configEnv = dotenv.parse(fs.readFileSync(`.env.${env|| 'development'}`))
for (let k in configEnv) {
if(configEnv.hasOwnProperty(k)) {
process.env[k] = configEnv[k]
}
}
},
onErrors: () => {
const notifier = require('node-notifier')
return (severity, errors) => {
if (severity !== 'error') return
const error = errors[0]
const filename = error.file && error.file.split('!').pop()
notifier.notify({
title: packageConfig.name,
message: severity + ': ' + error.name,
subtitle: filename || '',
icon: path.join(__dirname, 'logo.png')
})
}
}
}
webpack.config.js
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const copyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// webpack4以上要添加VueLoaderPlugin
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const config = require('./config')
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
module.exports = (env, argv) => {
argv.env && (config.mode = "production")
config.hiddleEnv(argv.env)
return webpackConfigFun()
}
const webpackConfigFun = ()=> {
return {
mode: process.env.ENV === 'development' ? 'development': 'production',
entry: __dirname + "/src/main.js", // 打包入口
output: { // 出口文件
path: __dirname + "/dist", // 打包后存放的地方
filename: "bundle.js", // 打包后的文件命名
publicPath: '/'
},
devServer: config.devServer,
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js', // 重定向代理
"@": path.resolve(__dirname + "/src/") // 設(shè)置模塊基路徑
}
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.scss$/,
use: [
'style-loader', // 將 JS 字符串生成為 style 節(jié)點(diǎn)
'css-loader', // 將 CSS 轉(zhuǎn)化成 CommonJS 模塊
'sass-loader', // 將 Sass 編譯成 CSS
{
loader: "postcss-loader",//添加瀏覽器兼容后綴
options: {
plugins: [
require("autoprefixer")(),
]
}
},
{
loader: 'sass-resources-loader', // 絕對(duì)路徑引入主題色文件
options: {
resources: [
path.resolve(__dirname + "/src/assets/css/theme.scss"),
path.resolve(__dirname + "/src/assets/css/mixin.scss")
]
},
},
]
},
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.js$/,
loader: 'babel-loader',
include: /node_modules/
}
]
},
plugins: [
// v5.12以上的版本要添加patterns
new copyWebpackPlugin({
patterns: [
{
from:path.resolve(__dirname+'/static'),// 打包的靜態(tài)資源目錄地址 不經(jīng)過(guò) webpack,需要通過(guò)絕對(duì)路徑來(lái)引用
to:'static' // 打包到dist下面的static
}
]
}),
new VueLoaderPlugin(),
new CleanWebpackPlugin(), // 每次打包都先刪除打包目錄
new FriendlyErrorsWebpackPlugin({
compilationSuccessInfo: {
messages: [`Your application is running here: http://${config.devServer.host}:${config.devServer.port}`],
},
onErrors: process.env.ENV === 'development' ? config.devServer.onErrors: undefined // 是否開(kāi)啟錯(cuò)誤捕獲
}),
new HtmlWebpackPlugin({
title: 'webpack',
minify: { // 壓縮HTML文件
removeComments: true, // 移除HTML中的注釋
collapseWhitespace: true, // 刪除空白符與換行符
minifyCSS: true// 壓縮內(nèi)聯(lián)css
},
filename: 'index.html', // 輸出的模板名字
template: 'index.html' // 模板路徑
})
],
stats: process.env.ENV === 'development' ? 'none': true
}
}
package.json
"scripts": {
"dev": "webpack-dev-server",
"build": "webpack --env production",
"build:beta": "webpack --env beta"
}
默認(rèn)加載.env.development宜猜,如果需要加載其他則需要--env xxx 對(duì)應(yīng)環(huán)境變量.env.xxx
mode有三個(gè)模式泼返,減少了webpack大量的配置
Mode: development
Mode: production
Mode: none 這個(gè)基本很少用
環(huán)境變量的核心是利用webpack更改mode變量的行為
var config = {
entry: './app.js'
//...
};
module.exports = (env, argv) => {
if (argv.mode === 'development') {
config.devtool = 'source-map';
}
if (argv.mode === 'production') {
//...
}
return config;
};
webpack文檔地址:webpack配置
到這里環(huán)境變量就初步配置完成了,后面還有一些webpack優(yōu)化姨拥,兼容绅喉,緩存的東西
未完待續(xù)。叫乌。柴罐。這個(gè)項(xiàng)目會(huì)一直更新直到功能完善
項(xiàng)目源碼:vue-webpack4