vue 项目总结 pc端 stepOne
首先需要说明的是,我用的是 vue 2.x的语法,vuea-cli 用的是3.x
1.用vue-cli 3.x 构建项目
首先你需要下载node (Vue CLI requiresNode.jsversion 8.9 or above (8.11.0+ recommended) 我想这句话就不用翻译了,大家应该都看的懂)
npm install -g @vue/cli (下载3.x脚手架)
vue --version (查看当前版本)
vue create hello-world (创建一个名字为hello-world的vue项目)
更多问题请参考官网:https://cli.vuejs.org/guide/installation.html
2.vue-cli 2.x 与 3.x 的区别
其实我觉得最大的区别就是,3.x 需要自己手动去写一些配置文件,比如跟src 同级的vue.config.js(主要是做些打包输出路径的配置及下载的一些plugins 的配置 和 是否开启eslint保存检测 ) 而这个 vue.config.js 打包的时候是必须要有的 ,不然后台发布到线上会或由于路径问题找不到一些文件
更多问题请参考官网 :https://cli.vuejs.org/zh/config/#lintonsave
mock数据:
2.x: config---->index.js
proxyTable: {
'/api':{
target:"http://localhost:8081",
pathRewrite:{
'^/api':'/static/mock'
}
}
},
然后在mock 文件夹下 建一些json 文件夹 就可以了
3.x: JSON Server
npm install -g json-server (先下载相应依赖)
建一个 db.json (跟src 平级)
然后 npm run json:server 就可以开始mock数据了
更多可以参考:https://github.com/typicode/json-server
3.api 路径的动态配置
const environment = "dev"; //需要根据环境进行修改 “dev”、“preview”、“prod”
export default {
getHost() { //这是后台api 路径
switch (environment) {
case "dev": //开发
return "xxx";
case "preview": //预发布
return "xxx";
case "prod": //正式
return "xxx";
}
},
getHref(){ //这是网页线上地址
switch (environment) {
case "dev": //开发
return "xxxx"
case "preview": //预发布
return "xxx"
case "prod": //正式
return"xxx"
}
},
isDev:false, // 是否为开发版
getEnvironment(){
return environment
}
}
4.网络请求的封装
1.npm install axios(必须下载)
2.npm install qs(用途:1.将对象序列化,多个对象之间用&拼(qs.stringify(params)) 2.将序列化的内容拆分成一个个单一的对象(qs.parse(params))
3. api.js
axios.defaults.baseURL = getHost.getHost() + "/api"; //动态配置网络请求不同环境下的地址
<pre>
//封装获取数据
export function get(url, params = {}, context) { //context vue 实例
return new Promise((resolve, reject) => {
url += "?";
let keys = Object.keys(params); //处理对象,返回可枚举的属性数组
console.log('keys',keys)
for (let i = 0, length = keys.length; i < length; i++) {
if (i === 0) {
url += keys[i] + "=";
url += params[keys[i]];
} else {
url += "&" + keys[i] + "=";
url += params[keys[i]];
}
}
console.log('url',url)
if(userToken){
url += '&userToken='+ userToken;
}
url += "×tamp="+new Date().getTime(); //加了个时间戳
url = encodeURI(url);//对url 进行编码
axios.get(url).then(res => {
if (res.status === 200) { //http 请求成功
if (res.data.code === 1000) { //公司内部定义的 code 数据正常返回
resolve(res.data.result);
}else if(res.data.code === 2999){ //请求成功,但数据返回异常
xxx //具体不同公司不同code
} else {
xxxxx
}
} else {
let msgContent = res.data.msgContent;
if (!isEmpty(msgContent) && context) {
context.$toast(msgContent);
}
reject({
errorMsg: msgContent,
detail: res.data
});
}
} else {
reject({
errorMsg: res.statusText
})
}
}).catch(err => {
reject(err)
})
})
}
//封装发送数据
export function post(url, params = {}, context) {
if(userToken){
Object.assign(params,{userToken:userToken}); //合并两个对象
}
return new Promise((resolve, reject) => {
axios.post(url, qs.stringify(params)).then(res => { //序列化字符串
if (res.status === 200) {
if (res.data.code === 1000) {
resolve(res.data.result);
} else if(res.data.code === 2999){
if (getHost.isDev) {
xxxxxx
} else {
xxxxxx
}
} else {
let msgContent = res.data.msgContent;
if (!isEmpty(msgContent) && context) {
context.$toast(msgContent);
}
reject(res.data.result);
}
} else {
reject({
errorMsg: res.statusText
})
}
}).catch(err => {
reject(err)
})
})
}
//封装上传文件、图片
export function uploadFile(url, params = {}, context) {
if(userToken){
Object.assign(params,{userToken:userToken});
}
return new Promise((resolve, reject) => {
let formData = new FormData(); // 不支持ie8,ie9 formData里面存储的数据是以健值对的形式存在的
let keys = Object.keys(params);
for (let i = 0, length = keys.length; i < length; i++) {
formData.append(keys[i], params[keys[i]]);
}
let config = {
headers: {
'Content-Type': 'multipart/form-data' //此处需要添加这个headers
}
};
axios.post(url, formData, config).then(res => {
if (res.status === 200) {
if (res.data.code === 1000) {
resolve(res.data.result);
} else if(res.data.code === 2999){
if (getHost.isDev) {
xxxxx
} else {
xxxxxx
}
} else {
let msgContent = res.data.msgContent;
if (!isEmpty(msgContent) && context) {
context.$toast(msgContent);
}
reject(res.data.result);
}
} else {
reject({
errorMsg: res.statusText
})
}
}).catch(err => {
reject(err)
})
})
}
main.js
import {get,post,uploadFile} from '../../common/js/api.js'
Vue.prototype.$get = get;
Vue.prototype.$post = post;
Vue.prototype.$uploadFile = uploadFile;
xxx.vue 调用:
this.$post(url,codeLoginObj,this).then((response)=>{
})</pre>
5.vue-cli 3.x多页面配置
1.需要在vue.config.js里面进行配置:
<code>
const webpack = require('webpack')
module.exports = {
pages:{
index:{
entry:'./src/login/login.js', //page 入口文件
template:'public/index.html', //模版来源
filename:'index.html' //打包好的文件名称
},
views:{
entry:'./src/views/main.js',
template:'public/index.html',
filename:'view.html'
},
}
}
</code>
6.vue-cli 3.x 中使用jquery
1.npm install jquery --save
2.在vue.config.js 里面配置:
<code>
const webpack = require('webpack')
module.exports = {
configureWebpack: {
plugins: [
new webpack.ProvidePlugin({
$:"jquery",
jQuery:"jquery",
"windows.jQuery":"jquery"
})
]
}
}
3、一般安装成功后在package.json文件内的dependencies项中会有"jquery": "^1.12.4"(jquery的版本不同人下载可能会有差异)然后才里面找到此处:
找到env ,在里面 添加 jquery:true
4、在main.js中添加“import $ from 'jquery”
然后你就可以用jquery了(不过建议最好不要用,毕竟他们两个的设计理念不同)
7.结合elment-ui做一些表单验证
<code>
//1.validate.js
//验证手机号码
export function isvalidPhone(str) { //方法封装
const reg = /^1[3-9][0-9]{9}$/;
return reg.test(str)
}
......
import {isvalidPhone} from '../../../../common/js/validate.js' //方法调用
data(){
var mobile=(rule,value,callback)=>{ //validator: mobile 对应的方法
if(!isvalidPhone(value)){
callback(new Error('手机号格式有误'))
}else{
callback()
}
}
return{
bindMobileForm:{
mobile:'',
code:''
},
Rules:{
"mobile":[
{ required:true,message:"手机号不能为空",trigger:"blur"},
{ required: true, trigger:'blur',validator: mobile}
],
"code":[
{ required:true,message:"验证码不能为空",trigger:"blur"},
]
}
}
}
<el-form :model="bindMobileForm" ref="bindMobileForm" :rules="Rules" key="code"> </el-form>
this.$refs[bindMobileForm].validate((valid) => {
if (valid) {
//表单验证通过
}
});
</code>
8.样式穿透
1.<style scoped src="../../../../common/css/form.css"></style> //引入别的css
2.由于项目中使用了element-ui 有些样式死活覆盖不了,就用了
<style scoped>
.el-form-item >>>.el-form-item__error{
margin-left: 120px;
}
</style>
9. data里面引入图片
data(){
return {
logoImage:require('../../assets/image/logo_white.png')
}
}
10.页面跳转
1. <router-link :to="{name:'loginLink'}">登录</router-link></span>
2. this.$router.push('/loginIndex');
3.this.$router.go(-1); //返回上一页
11.pc端点击 enter 键登录
<el-form-item prop="accountName">
<el-input type="text" v-model.trim="accountForm.accountName" autocomplete="off" placeholder="请输入账号" clearable class="mobile"></el-input>
</el-form-item>
<el-form-item prop="password">
@keyup.enter.native="submitAccountForm('accountForm')">
</el-form-item>
submitAccountForm(accountForm) {
this.$refs[accountForm].validate((valid) => {
if (valid) {
}
});
},
11.路由重定向问题
router---> index.js
{
path: '/views',
beforeEnter(to, from, next) {
if (config.isDev) {
window.location = '/view.html'
} else {
window.location = '/h5/saas/index.html'
}
}
}
12.vuex
store.js:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
export const store = new Vuex.Store({
state:{
userId:''
},
getters:{
isLogin(state){
if (!state.userId) { //防止刷新页面获取不到
state.userId=sessionStorage.getItem('userId');
}
return state.userId
}
},
mutations:{
removeUserId(state,payload){ //更新 state 方法
sessionStorage.removeItem("userId");
state.userId = ''
}
}
})
调用:computed:{
companyInfo(){
return this.$store.state.enterpriseInfo
return this.$store.getters.getWelfareTotalNumber //这个地方取得是getters 刷新页面中也会有
}
},
主动更新:
this.$store.commit("authentEnterPrise",{companyData:companyObj});
13.判断是否需要登录
login.js
router.beforeEach((to, from, next) => {
/* 判断该路由是否需要登录权限 */
if(to.matched.some(record => record.meta.requiresAuth)){
//是否登录
if(!sessionStorage.getItem('userId')){
next({
path: '/loginIndex',
query: { redirect: to.fullPath }
})
}else{
next()
}
}else{
next();
}
})
views router.js
routes: [
{
path: '*',
redirect:'/views',
meta: {
keepAlive: true,
requiresAuth: true
}
}
]
14.data里面的数据
data(){
return{
userId:sessionStorage.getItem('userId') //可以直接取缓存
}
}
15.组件间数据传递
1.子组件通知父组件
子组件内
this.$emit('resetInputValue', this.searchCondition);
父组件
<my-filter-form @resetInputValue="onReset"></my-filter-form>
2.父组件向子组件 传参数
<my-page :totalPage="tableList.length" v-show="tableList.length >= 10" :pageSize="selectedListOnePageSize" ></my-page>
16.element-ui date-picker、el-dropdown
<div class="input-box f-left birthday-picker-box">
<span>生日</span>
<el-date-picker
v-model="birthdayStart"
name="birthdayStart"
format="MM-dd"
value-format="timestamp"
type="date"
placeholder=""
:editable="inputEditable"
:picker-options="pickerBirthdayBefore"
>
</el-date-picker>
<div class="line f-left">-</div>
</div>
<el-dropdown trigger="click" @command="handleCommand">
<span class="el-dropdown-link btn send time-send small-hand" id="custom-sebd-btn" :class="{active:customActive}">
{{custom}}
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="commandOne">生日当天发送</el-dropdown-item>
<el-dropdown-item :command="commandTwo">生日前一天发送</el-dropdown-item>
<el-dropdown-item :command="commandThr">生日前一星期发送</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
data() {
return {
pickerBirthdayBefore: {
//disabledDate通常是一个函数,
disabledDate: time => { // time.getTime() >= beginDateVal false 可用,true 不可用
let beginDateVal = this.birthdayEnd;
if (beginDateVal) {
return time.getTime() >= beginDateVal; //开始时间小于结束时间
return time.getTime() <= beginDateVal || time.getTime() > Date.now(); //结束时间大于开始时间且小于当前时间
return time.getTime() >= Date.now()-8.64e7 //今天及以后的日期都不能选
}
}
},
//自定义时间 发送 下拉
handleCommand(command){
this.custom = command.desc;
},
17.vue中使用动画 animate.css
1.npm install animate.css --save
2.import animate from 'animate.css'
3.<transition enter-active-class="animated fadeIn" leave-active-class="animated fadeOut"></transition>
18. 邮箱 部分用*代替
encryptedmailbox(email) {
if (String(email).indexOf('@') > 0) {
var str = email.split('@');
var _s = '';
var new_email = "";
if (str[0].length > 3) { //@前面多于3位
for (var i = 3; i < str[0].length; i++) {
_s += '*';
}
new_email= str[0].substr(0, 3) + _s + '@' + str[1];
}else{ //@前面小于等于于3位
for(var i = 1;i<str[0].length;i++){
_s+='*'
}
new_email = str[0].substr(0,1)+ _s + '@' + str[1]
}
}
return new_email;
}
19.三目运算符 双层if 判断:
{{item.errorField == 1 ? item.email== "" ? '邮箱为空' :"格式不正确" : item.email}}
20.动态绑定 class
:class="{'emailError':item.errorField == 1}" //emailError class 名称