vue的安装及创建一个新项目前一篇文章已经介绍过了,有需要的请看之前的文章
移动端项目
一、关于移动端的一些ui框架
1.Voinc 一个基于 vue.js 和 ionic 样式的 UI 框架(https://wangdahoo.github.io/vonic-documents/#/?id=%E4%BB%8B%E7%BB%8D)
2.Vux 基于WeUI和Vue(2.x)开发的移动端UI组件库 (https://vux.li/#/?id=%E7%AE%80%E4%BB%8B)
3. NutUI 京东轻量级移动端Vue组件库(https://nutui.jd.com/#/index)
4. .Mint UI 由饿了么前端团队推出的 Mint UI (http://mint-ui.github.io/docs/#/zh-cn2)
5. Vant是有赞前端团队基于有赞统一的规范实现的 Vue 组件库(https://github.com/youzan/zent)
6. Cube UI 滴滴 WebApp实现的移动端组件库(https://didi.github.io/cube-ui/#/zh-CN/docs/quick-start)
二、移动端适配
方案:lib-flexible会自动在html的head中添加一个meta name="viewport"的标签,同时会自动设置html的font-size为屏幕宽度除以10,也就是1rem等于html根节点的font-size。使用postcss-px2rem-exclude自动将css中的px转成rem
步骤:1.项目中引入lib-flexible npm install lib-flexible --save
2.在项目的入口main.js文件中引入lib-flexible import ‘lib-flexible/flexible.js’
3.安装postcss-px2rem-exclude npm install postcss-px2rem-exclude --save
4.在项目的根目录下找到文件.postcssrc.js,在里面添加如下代码
"postcss-px2rem-exclude": {
remUnit: 75,
exclude: /node_modules|folder_name/i // 忽略node_modules目录下的文件(引入的第三方ui的样式不会随之改变)
}
- 1
- 2
- 3
- 4
- 5
5.在依赖文件node_modules中找到postcss-px2rem-exclude文件中的lib下的index.js添加以下代码(引入的第三方ui的样式不会随之改变)
try {
var flag = options.exclude.includes('/')
if (flag) {
var arr = options.exclude.split('/')
options.exclude = new RegExp(arr[1], arr[2])
}
} catch (error) {}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
二.一 安装sass-loader(如果使用到了sass编译再安装,不使用无需安装)
npm install --save-dev sass-loader
//sass-loader依赖于node-sass ,所以继续安装node-sass
npm install --save-dev node-sass
//注意1.安装node-sass会报错的原因,一般是因为网慢,所以安装node-sass的时候可以使用cnpm安装
//注意2.sass-loader安装了但是编译还是报错,原因可能是sass-loader版本的问题(8.0.0),在package.json中把sass-loader的版本改成7.3.1,然后删除依赖项,重新安装依赖
- 1
- 2
- 3
- 4
- 5
三、给IDE装vue插件(我现在使用的是vscode,就以vscode为例)
1.在vscode扩展里搜索Vetur
2.安装完成进行配置
文件–>首选项–>用户代码片段–>点击新建代码片段–取名vue.json 确定
3.删除多余代码
4.粘贴下边的代码vue模板 (如果使用css预编译,则style的类型是scss,如不使用改为css)
{
"Print to console": {
"prefix": "vue",
"body": [
"<!-- $1 -->",
"<template>",
"<div class='$2'>$5</div>",
"</template>",
"",
"<script>",
"//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)",
"//例如:import 《组件名称》 from '《组件路径》';",
"",
"export default {",
"//import引入的组件需要注入到对象中才能使用",
"components: {},",
"data() {",
"//这里存放数据",
"return {",
"",
"};",
"},",
"//监听属性 类似于data概念",
"computed: {},",
"//监控data中的数据变化",
"watch: {},",
"//方法集合",
"methods: {",
"",
"},",
"//生命周期 - 创建完成(可以访问当前this实例)",
"created() {",
"",
"},",
"//生命周期 - 挂载完成(可以访问DOM元素)",
"mounted() {",
"",
"},",
"beforeCreate() {}, //生命周期 - 创建之前",
"beforeMount() {}, //生命周期 - 挂载之前",
"beforeUpdate() {}, //生命周期 - 更新之前",
"updated() {}, //生命周期 - 更新之后",
"beforeDestroy() {}, //生命周期 - 销毁之前",
"destroyed() {}, //生命周期 - 销毁完成",
"activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发",
"}",
"</script>",
"<style lang='scss' scoped>",
"//@import url($3); 引入公共css类",
"$4",
"</style>"
],
"description": "Log output to console"
}
}
- 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
6.上面代码中的 “prefix”: “vue”, 就是快捷键;保存好之后新建.vue结尾的文件试试(输入vue 按tab键就可以)
四、vue路由
1.定义组件,刚刚已经用模板定义了一个组件
2.在router index.js文件里引入刚刚写好的组件
我在这里是用的vue-router提供的按模块加载方式
下面2行代码,没有指定webpackChunkName,每个组件打包成一个js文件。
const ImportFuncDemo1 = () => import('../components/ImportFuncDemo1')
const ImportFuncDemo2 = () => import('../components/ImportFuncDemo2')
- 1
- 2
下面2行代码,指定了相同的webpackChunkName,会合并打包成一个js文件。
// const ImportFuncDemo = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/ImportFuncDemo')
// const ImportFuncDemo2 = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/ImportFuncDemo2')
- 1
- 2
3.配置
import Vue from "vue";
import VueRouter from "vue-router";
Vue.use(VueRouter)
// 按模块加载...
const HelloWorld= () => import('../components/HelloWorld')
const routes=[{
path: '/',
redirect: '/HelloWorld',
name:'HelloWorld'
},{
path:'/HelloWorld',
component: HelloWorld,
name:'HelloWorld'
}
]
const router = new VueRouter({
routes,
mode: 'history',
// vuex的严格模式
strict: process.env.NODE_ENV !== 'production',
// 这个整体做的是:在路由的history模式下,一些列表页利用缓存模式来记录位置(一般是返回不刷新,前进刷新),一般用了scrollBehavior,
//同时还用keep-alive(缓存),activated(缓存下触发的钩子)配合做列表页的返回记录位置。缓存模式也有坑,就是何时清除缓存,一般是从新进入页面就清除。
//回到主题,滚动行为就是:例如一个列表页,滑动了很多,点进去、再返回记录刚刚的位置
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
if (from.meta.keepAlive) {
from.meta.savedPosition = document.body.scrollTop;
}
return { x: 0, y: to.meta.savedPosition ||0}
}
}
})
export default router
- 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
4.路由文件注入到main.js文件中
import Vue from 'vue';
import router from './router/index';
import 'lib-flexible/flexible.js';
import App from './App'
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
5.在 app.vue里配置router-view
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
- 1
- 2
- 3
- 4
- 5
6.路由跳转及传参方式
1.$router为VueRouter实例,想要导航到不同URL,则使用$router.push方法
2.$route为当前router跳转对象,里面可以获取name、path、query、params等
(1)设置动态路由
{
path:'/shopDetails/:id',
component: shopDetails,
name:'shopDetails'
}
跳转this.$router.push('/shopDetails/'+id)
获取id通过 this.$route.params.id
(2)通过params携带参数 ,路由属性中的name来匹配路由
{
path:'/shopDetails',
component: shopDetails,
name:'shopDetails'
}
跳转 this.$router.push({name:'shopDetails',params:{id:id}})
获取 this.$route.params.id
(3)通过path匹配路由,query携带参数 这种情况下 query传递的参数会显示在url后面?id=?
{
path:'/shopDetails',
component: shopDetails,
name:'shopDetails'
}
跳转 this.$router.push({path:'/shopDetails',query:{id:id}})
获取 this.$route.query.id
- 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
7.query和params的区别
1.动态路由和query属性传值 页面刷新参数不会丢失, params会丢失 2.动态路由一般用来传一个参数时居多(如详情页的id), query、params可以传递一个也可以传递多个参数 。
2.直白的来说query相当于get请求,页面跳转的时候,可以在地址栏看到请求参数,而params相当于post请求,参数不会再地址栏中显示
8.路由的模式
五、axios请求
axios文档上的教程在这里就不重复叙述了,需要的看文档,我在这里贴下我的使用教程
axios文档地址:https://www.npmjs.com/package/axios
1.安装 npm install axios
2.在utils下新建一个axios文件用来配置axios
import axios from 'axios';
const qs = require('qs');
const service=axios.create({
baseURL: process.env.BASE_API, //请求公共地址,baseURL`将被添加到`url`,所以后边请求只需api的方式即可
timeout: 5000, //请求响应时间
})
// 是否携带cookie信息,默认为false,根据项目需求设置
service.defaults.withCredentials = true;
// 添加一个请求拦截器
service.interceptors.request.use(function (config) {
// 对请求数据做些事
if(config.method === 'post'){ //post传参序列化
config.data = qs.stringify(config.data);
}
return config;
}, function (error) {
return Promise.reject(error);
});
// 添加一个响应拦截器
service.interceptors.response.use(function (response) {
// 对响应数据做些事
return response;
}, function (error) {
console.log(error)
// Do something with response error
return Promise.reject(error);
});
export default service;
- 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
3.新建一个api文件,这里边存放所有的请求函数,方便查找管理
import axios from '../utils/axios';
// 首页banner
export function banner(){
return axios({
url:'/wxchat/xxx.action', //请求的地址
method:'POST'
})
}
// post请求带参数
export function qiang(activityStatusType){
return axios({
url:'/wxchat/xxx.action',
method:'POST',
data:{activityStatusType:activityStatusType}
})
}
// get请求
export function moneys(){
return axios({
url: '/sd-web/xxx',
method: 'get'
});
}
// get请求带参数
export function moneys(ids){
return axios({
url: '/sd-web/xxx',
method: 'get',
params:{id:ids}
});
}
- 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
4.页面实际应用
import {banner} from '../../api/api'; //在页面上引入需要的请求函数
//在生命周期函数或者需要的方法里运用
mounted() {
banner().then(response => {
this.banner=response.data.data.SlAdvertisTabList
}).catch(err=>{
console.log(err)
});
},
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
5.跨域配置
(1)在config文件夹写的index.js里配置跨域
proxyTable: {
'/wxchat': {
target: 'xxx', //目标接口域名
changeOrigin: true, //是否跨域
secure: false, //target默认情况下,不接受运行在HTTPS上,且使用了无效证书的后端服务器。如果你想要接受, 则需设置该项为false
// pathRewrite: { // 如果接口本身没有/wxchat需要通过pathRewrite来重写了地址 重写接口
// '^/wxchat: ' '
// }
}
},
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
注:我写的这个项目里,本身存在/wechat通用前缀,所以我没有用pathRewrite重写,如果你们接口里没有通用前缀的话,是要进行重写的
上面配置中,’^/wxchat’ 其实是一个正则表达式
‘^/wxchat’ 应该拆分成 ‘^’ 和 ‘/wxchat’ 两个字符串,其中 ‘^’ 匹配的是字符串最开始的位置。
‘/wxchat’: {}, 就是告诉node, 我接口只要是’/wxchat’开头的才用代理.所以你的接口就要这么写/wxchat/xx/xx. 最后代理的路径就是 http://xxx.xx.com/wxchat/xx/xx.
可是不对啊, 我正确的接口路径里面没有/wxchat啊. 所以就需要 pathRewrite,用’’^/wxchat’’:’’, 把’/wxchat’去掉, 这样既能有正确标识, 又能在请求接口的时候去掉wxchat
接口没有通用前缀的开发环境的配置
'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
BASE_API: '"/wechat"', //在跨域里配置的//wechat,接口里就不用写通用前缀了,就按照正常接口写就可以了
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
接口有通用前缀的开发环境配置
'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
BASE_API: '""', //这里为空就好了,通用前缀就写在接口里
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
六、vuex的使用
关于vuex的使用 ,什么时候该使用vuex? 当你觉得需要全局的状态,但是通过别的方式又太麻烦的时候,你就需要了,比如说我现在做的一个app项目,header组件是一个公共组件,那么header组件里边的标题怎么能随着页面的跳转而改变呢,vuex就可以做到 (个人理解,有不同理解的可以评论)
缺点:vuex页面重新刷新,数据会丢失 解决方法:存本地,state数据可以从本地获取
1.vuex中,有默认的五种基本的对象:
- state 全局要访问的值
- getters 实时监听state值的变化,对数据获取之前的再次编译,可以理解为state的计算属性。
- mutations 改变state里的初始值 同步的
- actions 异步触发mutations里面的方法
- modules:store的子模块,为了开发大型项目,方便状态管理而使用的。这里我们就不解释了,用起来和上面的一样。
2.下载安装vuex npm install vuex --save
3.在src下新建一个store文件夹创建一个index.js 用来配置.
4.引入vue,vuex,使用use全局注入插件
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store=new Vuex.Store({
// 设置全局要访问的state值
state:{
title:'测试标题', //头部标题
}
})
export default store;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
5.在main.js里引入store,并全局注入
import Vue from 'vue';
import router from './router/index';
import store from './store';
import 'lib-flexible/flexible.js';
import App from './App'
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
6.在任意组件内测试刚刚写的标题
<template>
<div class='orderDetails'>
{{$store.state.title}}
</div>
</template>
- 1
- 2
- 3
- 4
- 5
7.如图所示,刚刚的标题已经设置成功了
8.上图所示已经成功设置了标题,那怎么让它的标题改变呢,就要用到mutations对象了,我们在mutations对象里定义一个改变标题的方法,mutations里面的参数,第一个默认为state,接下来的为自定义参数。
// 改变state里的初始值 同步的
mutations :{
TITLE(state,title){
return state.title=title
},
},
- 1
- 2
- 3
- 4
- 5
- 6
9.看下测试结
9.从图上可以看出这个已经实现了,接下来看异步操作
// 异步触发mutations里面的方法 在外部组件里进行全局执行actions里面方法的时候,你只需要用执行this.$store.dispatch('title',132) 这样就可以全局改变改变标题的值了
actions:{
title({commit},title){
commit('TITLE',title)
},
}
- 1
- 2
- 3
- 4
- 5
- 6
就不上图了,跟之前同步操作是一样的结果
10.vuex同步和异步的区别
1、当点发送过快,页面中渲染的内容与state中的数据不一致,vuex里面的state变得慢,且不持续更新
2、action中是可以做到页面中state中数据保持一致
3、当你的操作行为中含有异步操作,比如向后台发送请求获取数据,就需要使用action的dispatch去完成了。其他使用commit即可。
七、打包上线
1.
执行 npm run build 命令
- 1
2.执行完成后,你会发现你的目录下多了一个dist的文件夹,这个就是打包的数据
3.打包之后出现页面空白的原因
3.1 css,js路径引用错误的问题
解决:到config文件夹中打开index.js文件。
文件里面有两个assetsPublicPath属性,更改第一个,也就是更改build里面的assetsPublicPath属性:
assetsPublicPath属性作用是指定编译发布的根目录,‘/’指的是项目的根目录 ,’./’指的是当前目录。
3.2 设置路由history模式
解决:改为hash或者直接把模式配置删除,让它默认的就行 。如果非要使用history模式的话,需要你在服务端加一个覆盖所有的情况的候选资源:如果URL匹配不到任何静态资源,则应该返回一个index.html,这个页面就是你app依赖页面。
3.3 在css中引入的背景图片的路径问题
解决:到build文件夹中打开util.js文件,添加路径代码