vue项目搭建

模仿搭建vue-cli

其实vue-cli本身就挺好用的,但是毕竟自己亲手搭的还是比较灵活,出了问题也方便解决,所以我们自己动手搭建一个脚手架

主要用到的工具

  • webpack
  • webpack-cli

package.json配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{
"name": "book",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "cross-env NODE_ENV=development webpack-dev-server",
"build": "cross-env NODE_ENV=production webpack"
},
"author": "",
"license": "ISC",
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-preset-es2015": "^6.24.1",
"config": "^3.0.1",
"cross-env": "^5.2.0",
"css-loader": "^2.1.0",
"file-loader": "^3.0.1",
"gulp": "^4.0.0",
"html-webpack-plugin": "^3.2.0",
"os": "^0.1.1",
"style-loader": "^0.23.1",
"url-loader": "^1.1.2",
"vue": "^2.6.6",
"vue-loader": "^15.6.2",
"vue-router": "^3.0.2",
"vue-template-compiler": "^2.6.6",
"webpack": "^4.29.3",
"webpack-cli": "^3.2.3",
"webpack-dev-server": "^3.1.14"
},
"dependencies": {
"axios": "^0.18.0",
"element-ui": "^2.5.4",
"qs": "^6.6.0"
}
}

webpack.config.js内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
const webpack = require('webpack');
const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const dev_mode = (process.env.NODE_ENV == 'development');
const config =require('config');
const os = require('os');

//获取ip
//注意匹配127.0.0.1的情况
let host = '';
let obj = os.networkInterfaces();
for (n in obj) {
obj[n].map(v => {
/^[0-9]{1,}\.[0-9]{2,}\.[0-9]{1,}\.[0-9]{1,}$/.test(v.address) && (host = v.address)
})
}
module.exports = {
entry:path.join(__dirname,'src/main.js'),
output:{
filename:'index.js',
path:path.resolve(__dirname,'dist')
},
mode: dev_mode ? 'development' : 'production',
module:{
rules:[
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
},
{
test: /\.vue$/,
loader:'vue-loader'
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(gif|jpg|jpeg|woff(2)?|ttf|eot|svg)/,
use: [
{
loader: 'url-loader',
options: {
limit: 1024,
name: '[name].[hash:6].[ext]'
}
},
]
}
]
},
devServer: {
contentBase: path.join(__dirname,'src'),
watchContentBase: true,
hot: true,
port: config.port,
host: '0.0.0.0',
proxy: {
'/index.php': {
target:"http://guying.club",
changeOrigin: true,
secure: false,
},
}
},
plugins:[
new VueLoaderPlugin(),
new HtmlWebpackPlugin(),
]
}

if(dev_mode){
module.exports.plugins = module.exports.plugins.concat([
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
'LOCAL_ADDRESS': JSON.stringify(`http://${host}:${config.port}`)
})
])
}

然后是自己封装的axios请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/**axios封装
* 请求拦截、相应拦截、错误统一处理
*/
import axios from 'axios';
import QS from 'qs';

// 环境的切换
if (process.env.NODE_ENV == 'development') {
axios.defaults.baseURL = LOCAL_ADDRESS;
} else if (process.env.NODE_ENV == 'debug') {
axios.defaults.baseURL = '';
} else if (process.env.NODE_ENV == 'production') {
axios.defaults.baseURL = '';
}



// 请求超时时间
axios.defaults.timeout = 10000;

// post请求头
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';

// 请求拦截器
axios.interceptors.request.use(
config => {
// 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了
// 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
return config;
},
error => {
return Promise.error(error);
})

// 响应拦截器
axios.interceptors.response.use(
response => {
if (response.status === 200) {
return Promise.resolve(response);
} else {
return Promise.reject(response);
}
},
//服务器状态码不是200的情况
error => {
if (error.response && error.response.status) {
switch (error.response.status) {
// 401: 未登录
// 未登录则跳转登录页面,并携带当前页面的路径
// 在登录成功后返回当前页面,这一步需要在登录页操作。
case 401:
router.replace({
path: '/login',
query: { redirect: router.currentRoute.fullPath }
});
break;
// 403 token过期
// 登录过期对用户进行提示
// 清除本地token和清空vuex中token对象
// 跳转登录页面
case 403:
Toast({
message: '登录过期,请重新登录',
duration: 1000,
forbidClick: true
});
// 清除token
localStorage.removeItem('token');
store.commit('loginSuccess', null);
// 跳转登录页面,并将要浏览的页面fullPath传过去,登录成功后跳转需要访问的页面
setTimeout(() => {
router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
});
}, 1000);
break;
// 404请求不存在
case 404:
Toast({
message: '网络请求不存在',
duration: 1500,
forbidClick: true
});
break;
// 其他错误,直接抛出错误提示
default:
Toast({
message: error.response.data.message,
duration: 1500,
forbidClick: true
});
}
return Promise.reject(error.response);
}
}
);
/**
* get方法,对应get请求
* @param {String} url [请求的url地址]
* @param {Object} params [请求时携带的参数]
*/
export function get(url, params){
return new Promise((resolve, reject) =>{
axios.get(url, {
params: params
})
.then(res => {
resolve(res.data);
})
.catch(err => {
reject(err.data)
})
});
}
/**
* post方法,对应post请求
* @param {String} url [请求的url地址]
* @param {Object} params [请求时携带的参数]
*/
export function post(url, params) {
return new Promise((resolve, reject) => {
axios.post(url, QS.stringify(params))
.then(res => {
resolve(res.data);
})
.catch(err => {
reject(err.data)
})
});
}
  • 初步搭建完成,可以使用了
  • 如果有问题,后续补充

使用了vue-router后页面地址栏路径改变后刷新页面报错

这个主要时由于设置了vue-router为history模式,找不到路由下面的index.html页面,解决方式为配置nginx服务器的try_files,
本地解决方式为配置webpack-dev-server,增加 -history-api-fallback

使用vue-router以后地址栏改变找不到静态资源

配置webpack>output>publicPath:’/‘