vue基础
template 是一个标签但是在页面不显示 vue自带的标签
设计模式(MVC/MVP/MVVM)的区别
Vue.js(读音 /vjuː/, 类似于 view)是一个构建数据驱动的 web 界面的渐进式MVVM框架。
设计模式(MVC/MVP/MVVM)的对比
MVC (Model View Controller ):
1.视图(View):用户界面。
2.控制器(Controller):业务逻辑
3.模型(Model):数据保存
MVC特点:
1、用户可以向 View 发送指令(DOM 事件),再由 View 直接要求 Model 改变状态。
2、用户也可以直接向 Controller 发送指令,再由 Controller 发送给 View。
3、Controller 非常薄,只起到路由的作用,而 View 非常厚,业务逻辑都部署在 View。
MVP(Model View Presenter):
MVP 模式将 Controller 改名为 Presenter,同时改变了通信方向。
MVP特点:
1、各部分之间的通信,都是双向的。
2、View 与 Model 不发生联系,都通过 Presenter 传递。
3、 View 非常薄,不部署任何业务逻辑,称为"被动视图"(Passive View),即没有任何主动性,而 Presenter非常厚,所有逻辑都部署在那里。
MVVM( Model-View-ViewModel ):
MVVM 模式将 Presenter 改名为 ViewModel,基本上与 MVP 模式完全一致。
MVVM特点:
唯一的区别是,它采用双向绑定(data-binding):View的变动,自动反映在 ViewModel,反之亦然。VUE采用这种模式。
VUE初相识
Vue.js(读音 /vjuː/, 类似于 view)是一个构建数据驱动的 web 界面的渐进式MVVM框架。正式发布于2014年2月,2016年4月,发布2.0版本,创造者-尤雨溪。
vue官网:https://cn.vuejs.org/
Vue的优点:
1、性能好
2、简单易用
3、前后端分离
4、单页面应用(SPA)用户体验好,还可以做转场动画
Vue的缺点:
1、不适合seo优化,解决方案服务端渲染(SSR)。
2、模板模式开发,封装的比较厉害,代码报错信息不明确。
3、适合单人开发,适合中小型项目。
VUE数据驱动和双向绑定
安装vue
CDN引入:
对于制作原型或学习,你可以这样使用最新版本:
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
对于生产环境,我们推荐链接到一个明确的版本号和构建文件,以避免新版本造成的不可预期的破坏:
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
vue-cli3脚手架安装:
npm install -g @vue/cli
创建项目:
vue create 项目目录
初始化
<div id="app">
{{name}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
new Vue({
el:"#app",//el根元素名称
//初始化数据源方式一(推荐)
data(){
return {
name:"张三"
}
}
});
<div id="app">
{{name}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
new Vue({
el:"#app",
//初始化数据源方式二
data:{
name:"张三"
}
});
data为什么要用一个方法而不用对象
因为vue是单页面应用,是由多个组件组成的,每个组件里面都有data,如果data是个对象,对象是引用类型,一个组件里面的data值改变了,其他组件的值也都会改变,为了避免出现互相引用的问题,我们要用方法来初始化数据形成一个作用域,防止各个组件互相引用
vue的数据驱动和双向绑定
v-model:双向绑定
v-cloak:解决加载闪烁时出现vue标签或指令符号
双向绑定的原理
实现双向绑定的原理就是利用了 Object.defineProperty() 这个方法重新定义了对象获取属性值(get)和设置属性值(set)的操作来实现的。
<input type="text">
<span id="text"></span>
let data={};
let val='';
let text=document.getElementById("text");
Object.defineProperty(data,"value",{
get:()=>{
return val;
},
set:(pVal)=>{
val=pVal;
text.innerHTML=pVal;
}
});
window.addEventListener("input",(e)=>{
data.value=e.target.value;
console.log(data.value);//如果get方法没有返回值,获取的是undefined
})
vue 中css样式使用
在vue中使用css样式:
普通写法:
<div class=“box”></div>
对象语法:
<div :class=“{box:true,show:true}”></div>
数组语法:
<div :class=“[box,show]”></div>
内联样式
对象写法:
<div :style=“{fontSize:’14px’,width:’100px;’,height:’100px’,badkgroundColor:’#FF0000’}”></div>
数组写法:
<div :style=“[baseStyles, fonts]”></div>
data(){
return {
baseStyles:{
width:'100px',
height:'100px',
backgroundColor:'#FF0000'
},
fonts:{
fontSize:'14px',
color:'#FFFFFF'
}
}
}
模板语法与常用指令
v-cloak:防止闪烁解决显示vue标签
v-model:多用于表单元素实现双向数据绑定
v-for:列表渲染
v-show:显示内容
v-if:显示与隐藏 (dom元素的删除添加默认值为false)
v-else-if:必须和v-if连用
v-else:必须和v-if连用 不能单独使用 否则报错 模板编译错误
v-bind:动态绑定 作用: 及时对页面的数据进行更改,缩写:
v-html:解析html标签
事件处理
v-on:click 给标签绑定函数,可以缩写为@,例如绑定一个点击函数 函数必须写在methods里面
v-on:click.stop:阻止冒泡事件
v-on:click.once:点击事件只允许一次
v-on:mouseover:鼠标移动元素上
v-on:mouseout:鼠标离开元素
v-on:mousemove:鼠标移动
v-on:touchstart:触摸开始
v-on:touchmove:触摸移动
v-on:touchend:触摸结束
v-on:keyup:键盘事件
v-on:keyup.enter:回车键
computed计算属性
computed用来监控自己定义的变量,该变量不在data里面声明,直接在computed里面定义,然后就可以在页面上进行双向数据绑定展示出结果,可以做逻辑处理的变量。
举例:购物车里面的商品列表和总金额之间的关系,只要商品列表里面的商品数量发生变化,或减少或增多或删除商品,总金额都应该发生变化。这里的这个总金额使用computed属性来进行计算是最好的选择。
<div id="app" v-cloak>
商品:html5
价格:{{price}}元
数量:<button type="button" @click="decrease()">减少</button>{{amount}}<button type="button" @click="increase()">增加</button>
金额:{{total}}元
</div>
new Vue({
el:"#app",
data(){
return {
amount:1,
price:10
}
},
computed:{
total(){
return this.amount*this.price
}
},
methods:{
//增加数量
increase(){
this.amount++;
},
//减少数量
decrease(){
if(this.amount>1){
this.amount--
}
}
}
});
watch监听
watch主要用于监控vue实例的变化,它可以监控一个data里面的数据,computed,路由的变化。
特点:
1、监听获取两个值一个是新值,另一个是旧值
2、可以深度监听
3、可以利用watch监听路由的特性做转场动画
<div id="app" v-cloak>
<button @click="name='李四'">{{name}}</button>
<button @click="classify.title='品牌男装'">{{classify.title}}</button>
<button @click="classify.children.title='下装'" />{{classify.children.title}}</div>
</div>
new Vue({
el:"#app",
data(){
return {
name:"张三",
classify:{
title:"大妈女装",
children:{
title:"上装",
children:{
title:"韩都衣舍大妈女装"
}
}
}
}
},
computed:{
getName(){
return this.name;
}
},
watch:{
//监听data里面的值
name(newVal,oldVal){
console.log("newVal:"+newVal,"oldVal:"+oldVal);
},
//监听computed里面的值
getName(newVal,oldVal){
console.log("computed-newVal:"+newVal,"computed-oldVal:"+oldVal);
},
//引用类型监听
"classify.title"(newVal,oldVal){
console.log(newVal,oldVal);
},
//深度监听
classify:{
handler(val){
console.log(JSON.stringify(val));
},
deep:true
}
}
});
全局组件
Vue.component(’组件名称’,’组件’)
组件包括:
template
data
methods
生命周期钩子函数
computed
watch
…….
//定义公共组件
let PublicComponent={
template:`
<div>
<span style="font-size:16px;color:#FF0000">我是公共组件</span>
</div>
`
};
//首页
let HomePage={
template:`
<div>
我是{{name}}
<PublicComp></PublicComp>
</div>
`,
data(){
return {
name:"首页"
}
}
};
//全局注册组件
Vue.component("PublicComp",PublicComponent);
new Vue({
el:"#app",
//挂载要使用的模板
components:{
HomePage
},
//模板规范必须只能有一个唯一的根元素标签
template:`
<div>
<PublicComp></PublicComp>
<HomePage></HomePage>
我是vue模板
</div>
`
});
全局filter过滤
Vue.filter("increase",function(value,num1,num2){
return parseInt(value)+parseInt(num1)+parseInt(num2);
});
var App={
data:function(){
return {
count:1
}
},
template:`<div>数量:{{count|increase(10,20)}}</div>`
};
局部filter过滤
var App={
data:function(){
return {
count:1
}
},
filters:{
formatDate:function(val){
if(val!==''){
val=val.split("-");
val=val[0]+"年"+val[1]+"月"+val[2]+"日"
}
return val;
}
},
template:`<div>日期:{{"2019-09-11"|formatDate}}</div>`
};
父子组件传值
父组件给子组件传值:
<ChildComponent title="我是子组件"></ChildComponent>
子组件用props接受:
第一种方式:
props:[‘title’] //数组方式接收
第二种方式(官方推荐):
props:{
title:{
type:String //数据类型
required:true //是否必须传值,true:必须,false:可选
}
}
子组件给父组件传值:
子组件代码:
<button type="button" @click="sendParent()">给父组件传值</button>
methods:{
sendParent(){
this.$emit("getChild","我是子组件传过来的值");
}
}
父组件代码:
<ChildComponent title=“我是子组件” @getChild="getParent($event)"></ChildComponent>
methods:{
getParent(data){//接收父组件HomeComponent传过来的值
console.log(data);
}
}
兄弟组件传值
//定义一个总线bus
let bus=new Vue;
//A组件
let AComponent={
template:`
<div>
<button type="button" @click="sendB()">给B组件传值</button>
</div>
`,
data(){
return {
}
},
methods:{
sendB(){
bus.$emit("getA","你好");//用$emit传值
}
}
}
let BComponent={
template:`
<div>
A组件传过来的值:{{text}}
</div>
`,
data(){
return{
text:""
}
},
created(){
//用$on接收
bus.$on("getA",(message)=>{
console.log(message);
this.text=message;
})
}
}
父组件与子组件实现双向绑定
方法一:
父组件
new Vue({
el:"#root",
data(){
return{
value:”张三”
}
},
components:{Comp:component},
template:`<div><Comp v-model="value"></Comp>{{value}}</div>`
}
)
子组件
const component={
template:`
<div>
<input type="text" v-model="currentValue">
</div>
`,
props: {
// 接收一个由父级组件传递过来的值
value: {
type: String
}
},
created(){
console.log(this.value)//接收父组件的值
},
computed:{
currentValue: {
// 动态计算currentValue的值
get:function() {
return this.value; // 将props中的value赋值给currentValue
},
set:function(val) {
this.$emit('input', val); // 通过$emit触发父组件
}
}
}
}
方法二:
父组件
new Vue({
el:"#root",
data(){
return{
name:"张三"
}
},
components:{Comp:component},
template:`
<div><Comp v-model="name"></Comp>{{name}}</div>
`
}
)
子组件
const component={
template:`
<div>
<input type="text" @input="changeName">
</div>
`,
model: {
prop:"name",//props里面的name
event:"change"//$emit里面的事件change
},
props: {
// 接收一个由父级组件传递过来的值
name: {
type: String
}
},
methods:{
changeName(e){
this.$emit("change",e.target.value);
}
}
}
ref的使用
<div class="box" ref="box1">box1</div>
<div class="box" ref="box2">box2</div>
<div class="box" ref="box3">box3</div>
console.log("当前:"+this.$refs.box1.innerHTML);
//父div
console.log("父div:"+this.$refs.box1.parentNode.innerHTML);
//下一个元素
console.log("下一个元素:"+this.$refs.box1.nextElementSibling.innerHTML);
//上一个元素
console.log("上一个元素:"+this.$refs.box2.previousElementSibling.innerHTML);
父组件调用子组件里面的方法:
<ChildComponent ref="child"></ChildComponent>
1、给子组件加上ref=“child”
2、在父组件使用:
this.$refs.child.send();//send()就是子组件里面的方法
Mixins混入
混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。
let base={
data(){
return {
visible:true
}
},
methods:{
toggle(){
this.visible=!this.visible
}
}
};
let Acomponent={
template:`
<div>
<button type="button" @click="toggle()">显示/隐藏a组件</button>
<div v-show="visible">我是A组件</div>
</div>
`,
mixins:[base]
};
let Bcomponent={
template:`
<div>
<button type="button" @click="toggle()">显示/隐藏b组件</button>
<div v-show="visible">我是b组件</div>
</div>
`,
data(){
return {
visible:true //覆盖mixins里面的visible
}
},
mixins:[base]
};
slot插槽
可以获取组件里的内容。
应用场景:自定义组件使用,比如自定义button按钮。
let ButtonComponent={
template:`
<button type="button" class="btn"><slot></slot></button>
`,
mounted:function(){
//获取插槽里的内容
if(this.$slots.default!=null){
console.log(this.$slots.default[0].elm.innerHTML);
}
},
};
new Vue({
el:"#app",
components:{
'my-button':ButtonComponent
},
template:`<div>
<my-button>提交</my-button>
</div>`
})
transition 过度/动画
<transition name=“fade”>
<div class=“box”></div>
</transistion>
.fade-enter{ } 进入过渡的开始状态,元素被插入时生效,只应用一帧后立即删除。(运动的初始状态)
.fade-enter-to{} (2.1.8版及以上) 定义进入过渡的结束状态。
.fade-enter-active{ } 定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。
.fade-leave{ } 离开过渡的开始状态,元素被删除时触发,只应用一帧后立即删除;
.fade-leave-to{} (2.1.8版及以上) 定义离开过渡的结束状态。
.fade-leave-active{ } 定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
配合animate.css实现动画效果:
animate官网:https://daneden.github.io/animate.css/
<button @click="isShow = !isShow">
Toggle render
</button>
<transition name="custom" enter-active-class="animated tada" leave-active-class="animated bounceInRight">
<div class="box" v-show="isShow"></div>
</transition>
data(){
return {
isShow:true
}
}
<transition-group name="slide">
<div class="banner-slide" v-show=”true" :key=”1"><img src=”1.jpg" alt=""></div>
<div class="banner-slide" v-show=”false" :key=”2"><img src=”2.jpg" alt=""></div>
</transition-group>
.slide-enter-active,.slide-leave-active{transition:all 1s;position:absolute;}
.slide-enter{
opacity:0;
}
.slide-enter-to{
opacity:1;
}
.slide-leave{
opacity:1;
}
.slide-leave-to{
opacity:0;
}
生命周期
beforeCreate:初始化之前
created:初始化完成(不能获取dom,一般用于获取ajax数据)
beforeMount:挂载之前
mounted:挂载完成之后
beforeUpdate:数据更新之前
updated:数据更新之后
beforeDestroy:解除绑定之前(页面离开)
destroyed:解除绑定之后(页面离开)
activated:keep-alive组件激活时调用。该钩子在服务器端渲染期间不被调用。用于性能优化缓存dom和数据。
deactivated:keep-alive组件停用时调用。该钩子在服务端渲染期间不被调用。
swiper插件和nextTick的使用
官网:https://www.swiper.com.cn/
nextTick:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
vue脚手架
安装vue脚手架
npm install -g @vue/cli
卸载vue脚手架
npm uninstall vue-cli -g
查看版本号
vue -V
帮助创建项目
vue --help
创建项目
vue create 项目名称
安装第三方插件
以前安装
npm install --save axios
现在可以用
vue add axios
.env配置全局变量和区分开发者环境和生产环境
全局变量
在根目录下面创建.env文件
内容:
VUE_APP_URL=http://vueshop.glbuys.com
注意命名规范:必须VUE_APP_开头
开发者全局变量
在根目录下创建.env.development文件
内容
VUE_APP_URL="http://vueshop.glbuys.com/api"
生产环境全局变量
在根目录下创建.env.production文件
内容
VUE_APP_URL="https://vueshop.glbuys.com/api"
读取值:
process.env.VUE_APP_URL
运行生成环境的文件
1、安装serve服务器
npm install -g serve
2、运行方式一:
serve -s dist
运行方式二:
cd dist
serve
vue.config.js的配置
根目录建立vue.config.js
在module.exports={}里面配置
1、配置根目录
publicPath:'/’
2、构建输出目录
outputDir:'dist'
3、 静态资源目录(js,css,image)
assetsDir:"assets"
4、是否开启eslint检测
lintOnSave:false //false不开启,有效值:true || false
服务器配置:
devServer:{
open:false, //是否启动打开浏览器
host:"localhost",//主机,0.0.0.0支持局域网地址,可以用真机测试
port:8080, //端口
https:false,//是否启动https
//配置跨域代理
proxy:{
"/api":{
target:"http://vueshop.glbuys.com/api",
changeOrigin:true,
pathRewrite:{
'^/api':""
}
}
}
}
mockjs的使用
mockjs的官网:http://mockjs.com/
mockjs是一个模拟json数据的测试工具。
安装mockjs
npm install mockjs --save-dev
新建mork.js
import Mock from 'mockjs';
Mock.mock('/api/news',"get",{
'data|10':[
{
'id|+1':1,
'title|+1':Mock.Random.cword(8,20),
'image|+1':Mock.Random.image('200x100', Mock.Random.color()),
'date|+1':Mock.Random.date('yyyy-MM-dd')
}
]
});
});
导入mock.js
import "./js/mock.js";
$.ajax({
url:"/api/news",
type:"get",
dataType:"json",
success:function(res){
console.log(res.news);
}
}
})
eslint的使用 --和严格模式差不多
官网:http://eslint.cn
eslint的作用:
1. 提供编码规范;
2. 提供自动检验代码的程序,并打印检验结果:告诉你哪一个文件哪一行代码不符合哪一条编码规范,方便你去修改代码。
最常用的两个命令:
/*eslint-disable*/ 禁用eslint检测
/*eslint-enable*/ 开启eslint检测
移动端布局及适配方式
viewport使用
<meta name="viewport" content="width=device-width,initial-scale=1.0, maximum-scale=1.0,user-scalable=no" />
width:设置layout viewport 的宽度,为一个正整数,或字符串"device-width"
initial-scale:设置页面的初始缩放值,为一个数字,可以带小数
minimum-scale:允许用户的最小缩放值, 为一个数字,可以带小数
maximum-scale:允许用户的最大缩放值,为一个数字,可以带小数
height:设置layout viewport 的高度,这个属性对我们并不重要,很少使用
user-scalable:是否允许用户进行缩放,值为"no"或"yes", no 代表不允许,yes代表允许
format-detection使用
<meta name=“format-detection” content=“telephone=no,email=no,date=no,address=no” />
telephone:电话
eamil:邮箱
date:日期
address:地址
移动端如何做适配?
采用rem作为布局单位。
什么是rem?
rem是CSS3新增的相对长度单位,是指相对于根元素html的font-size计算值的大小。
简单可理解为屏幕宽度的百分比。
rem与em的区别?
rem是针对根元素的html的font-size计算值大小计算的。
em是根据父元素的font-size大小计算的。
使用手淘flexible.js计算rem
比如:750px设计稿,那么1rem=75px, div的高是50px ,50px换算成rem公式:
50px/75px=0.7rem;
解决1px细线问题
问题出现原因:
在retina(视网膜)屏上面, devicePixelRatio(物理像素)这个值是2或3, 所以1px长度映射到物理像素上就有2px或3px那么长。
解决方案:
viewport + rem 方案
<meta name=“viewport” content=“initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no” />
接下来的任务就是js的动态修改缩放比 以及 实现rem根元素字体大小的设置。
var viewport = document.querySelector("meta[name=viewport]");
if (window.devicePixelRatio == 1) {
viewport.setAttribute('content', 'width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no')
}
if (window.devicePixelRatio == 2) {
viewport.setAttribute('content', 'width=device-width, initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no')
}
if (window.devicePixelRatio == 3) {
viewport.setAttribute('content', 'width=device-width, initial-scale=0.333333333, maximum-scale=0.333333333, minimum-scale=0.333333333, user-scalable=no')
}
var docEl = document.documentElement;
var fontsize = 10 * (docEl.clientWidth / 320) + 'px';
docEl.style.fontSize = fontsize;
面试解答:
判断devicePixelRatio物理像素如果为1,设置viewport缩放大小为1,如果为2设置viewport缩放大小为0.5,如果为3设置viewport缩放大小为0.33….。
自定义组件和axios与fetch的使用
自定义封装组件
注册组件
export default{
install(Vue){
Vue.component("my-button",Button);
}
}
使用组件
import Vue from 'vue';
import Button from '../components/button’
Vue.use(Button)
axios的介绍
axios是一款基于promise封装的库,可以运行在浏览器端和node环境中。 vue官方开发组放弃了对其官方库vue-resource的维护,直接推荐我们使用axios库。
官方文档:https://github.com/axios/axios
1、get请求
2、post请求
3、使用URLSearchParams或qs做post提交处理
3、图片上传
使用URlSearchParams兼容ie
1、安装url-search-params-polyfill
npm install url-search-params-polyfill --save
2、入口页面导入url-search-params-polyfill
import 'url-search-params-polyfill';
fetch的介绍
fetch是ajax2.0,新的浏览器现在支持Fetch API,可以无须其他库就能实现Ajax,使用的方式和axios差不多。
浏览器兼容性:
桌面浏览器
手机、平板电脑
fetch的使用
兼容低版本浏览器:https://github.com/github/fetch
安装:npm install whatwg-fetch --save
引入:import fetch from 'whatwg-fetch'
1、get请求
2、post请求
3、使用URLSearchParams或qs做post提交处理
3、图片上传
fetch的get使用
fetch("/api/v1/news",{
method:"get"
}).then((res)=>{
return res.json();
}).then(res=>{
this.newsList=res.data;
});
fetch("/proxy/home/user/pwdlogin?token=1ec949a15fb709370f",{
method:"post",
headers:{
'Content-Type':"application/x-www-form-urlencoded"
}, body:"cellphone="+encodeURIComponent(this.username)+"&password="+encodeURIComponent(this.password)+""
}).then(res=>res.json()).then(res=>{
console.log(res);
})
fetch的文件上传
let data=new FormData();
data.append("headfile",this.$refs["head"].files[0]);
fetch("/proxy/user/myinfo/formdatahead?token=1ec949a15fb709370f",{
method:"post",
body:data
}).then(res=>res.json()).then(res=>{
console.log(res);
});
vue的路由
安装vue-router
npm install --save vue-router
路由嵌套
{
path:”/goods”,
component: ()=>import(‘./views/goods.vue’),
redirect:’/goods/info’,//页面重定向
children:[//子路由
{
path:”info”,
component: ()=>import(‘./views/goods_info.vue’);
}
]
}
]
})
vue-router的配置
建立一个router.js文件,代码如下:
import Vue from 'vue';
import Router from 'vue-router';//引用路由
import HomePage from './pages/index';
Vue.use(Router);//装载路由
//路由实例化
let router=new Router({
mode:"hash", //1、hash哈希:有#号。2、history历史:没有#号
routes:[
{
path:"/",
name:"index",
component:HomePage
},
建立一个router.js文件,代码如下:
import Vue from 'vue';
import Router from 'vue-router';//引用路由
import HomePage from './pages/index';
Vue.use(Router);//装载路由
//路由实例化
let router=new Router({
mode:"hash", //1、hash哈希:有#号。2、history历史:没有#号
routes:[
{
path:"/",
name:"index",
component:HomePage
},
{
path:'/news',
name:"news",
component:()=>import("./pages/news")//路由懒加载
},
]
});
export default router;
动态路由
routes:[
{
path: '/news/:id',
name: 'news',
component: () => import('./views/News.vue'),
},
]
<router-link to=“/news/10">新闻页面</router-link>
动态路由与接收参数
路由跳转方式一:
<router-link to=“/about">About</router-link>
路由跳转方式二:
this.$router.push({path:’/about’})
push进入跳转历史记录
路由跳转方式三:
this.$router.replace({path:’/about’})
replace不进入跳转历史记录
路由传参方式一:
<router-link to=“/about?id=10">About</router-link>
路由传参方式二:
this.$router.push({name:’about’,params:{id:10}})
params:参数不显示url地址上
路由传参方式三(推荐):
this.$router.push({path:’/about’,query:{id:10}})
query:参数显示在地址上
接收参数方式一:
this.$route.params.id
接收参数方式二(推荐):
this.$route.query.id
返回上一级:
this.$router.go(-1)
路由钩子函数与路由认证
{
path:’/user/profile',
component: ()=>import(‘./user/profile.vue’),
meta: { title: '个人资料',auth:true }
}
auth:true表示这个页面需要进行会员认证,auth就是一个标识。
路由守卫
beforeEach:全局前置守卫
const router = new VueRouter({ ... })
router.beforeEach(function (to, from, next) {
if(to.meta.auth){
if(Boolean(store.state.isLogin)==true){//已登录
next();
}else{//未登录
next({”/login/index"});
}
}else{
next()
}
});
beforeEnter:路由独享的守卫(路由内钩子)
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
//….
}
}
]
})
组件内的守卫-------组件内的钩子
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用 // 可以访问组件实例 `this`
}
应用场景:
组件内的守卫(组件内钩子)在实际的开发过程中使用较少, 在实际的项目中beforeRouteLeave还算有用, 使用场景分别为一下三类情况:
(一) 清除当前组件中的定时器
当一个组件中有一个定时器时, 在路由进行切换的时候, 可使用beforeRouteLeave将定时器进行清楚, 以免占用内存(可以用destroyed生命周期钩子函数代替):
beforeRouteLeave (to, from, next) {
window.clearInterval(this.timer) //清楚定时器
next()
}
(二) 当页面中有未关闭的窗口, 或未保存的内容时, 阻止页面跳转
如果页面内有重要的信息需要用户保存后才能进行跳转, 或者有弹出框的情况. 应该阻止用户跳转。
beforeRouteLeave (to, from, next) {
if (this.isShow){
alert('必须关闭弹窗才能跳转页面');
next(false);
} else {
next();
}
}
(三) 保存相关内容到Vuex中或Session中
当用户需要跳转页面时, 可以将公用的信息保存到session或Vuex中(可以用destroyed生命周期钩子函数代替)。
beforeRouteLeave (to, from, next) {
localStorage.setItem(name, content); //保存到localStorage中
next()
}
history与hash模式的区别
hash模式:路由地址带#号。适合做后台管理系统
history模式:路由地址不带#号。适合做前端宣传页面。但是history模式有个问题就是刷新页面会出现404错误,解决方法需要配置服务器。
apache解决方案:
在根目录下新建.htaccess文件,内容如下:
<IfModule mod_rewrite.c>
Options Indexes FollowSymLinks ExecCGI
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>
nginx解决方案:
location / {
try_files $uri $uri/ /index.html;
}
Vuex状态管理
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。将数据分发给各个组件。
应用场景比如:购物车、会员登录等需要跨页面,跨组件实时传递数据的地方。
安装vuex
npm install --save vuex
vuex的使用
//导入vuex
import Vuex from 'vuex'
//启用vuex
Vue.use(Vuex)
//实例化对象
let store=new Vuex.Store({
state:{}//初始化数据
mutations:{}//同步操作方法
actions:{}//异步操作,用于操作mutations里面的方法,如果mutations里面的方法操作量大最好写在actions里面。
getters:{}//有时候我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤并计数
})
new Vue({
el: '#app',
router,
store,//挂载到vue里使用
components: { App },
template: '<App/>'
})
state mutations actions gettters modules的介绍与使用
state:定义vuex的数据源。
state:{
total:0,
users:[
{id:1,name:"张三",age:18},
{id:2,name:"李四",age:20},
{id:3,name:"王五",age:22},
{id:4,name:"赵六",age:25}
]
}
页面调用方式一:
this.$store.state.count
页面调用方式二:
使用辅助函数
import { mapState } from 'vuex'
computed:{
...mapState({
total:state=>state.total
})
}
mutions :同步方式的方法提交,将改变的值赋给state数据源。
mutations:{
increment(state,payload){
state.total = payload.count;
}
}
mutions :同步方式的方法提交,将改变的值赋给state数据源。
mutations:{
increment(state,payload){
state.total = payload.count;
}
}
this.$store.commit(“increment”,{count:10});
辅助函数提交
import { mapMutations } from 'vuex’
methods: {
//方式一
...mapMutations([
'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
]),
//方式二
...mapMutations({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
}
actions :异步方式的方法提交,用于操作mutions里面的方法,实际应用里面获取ajax异步数据。
actions:{
inc(conText,payload){
conText.commit("increment",payload);
}
}
辅助函数提交
import { mapActions } from 'vuex’
methods: {
//方式一
... mapActions ([
'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
]),
//方式二
... mapActions ({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
}
gettters :可以认为是 store 的计算属性,类似于computed,例如对列表进行过滤并计数。
getters:{
getUsers(state){
let aUsers=state.users.filter((res)=>{
return res.age>18;
})
return aUsers;
}
}
this.$store.getters.getUsers
辅助函数
import {mapGetters} from 'vuex’
computed: {
//方式一
...mapGetters([
'getUsers'
]),
//方式二
...mapGetters({
getUsers:'getUsers'
})
}
modules: 将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter。
const moduleA = {
namespaced:true,//命名空间
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
热门的ui库的使用
自定义指令
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
<input type=“text” placeholder=“用户名” v-focus />
// 注册一个局部指令 `v-position`
directives:{
position:{
bind:(el,binding)=>{
el.style.position="absolute";
el.style[binding.arg]=binding.value+"px";
}
}
}
<div v-position:left="'200'" class="box"></div>
钩子函数
bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
钩子函数参数
name:指令名,不包括 v- 前缀。
binding里面的参数:
value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。
oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。
modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
Mint-ui官网:http://mint-ui.github.io
Mint-ui介绍: Mint UI 包含丰富的 CSS 和 JS 组件,能够满足日常的移动端开发需要。通过它,可以快速构建出风格统一的页面,提升开发效率。
Element-ui官网: https://element.eleme.cn
Element-ui介绍:一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库。
Better-Scroll的使用
Better-Scroll官网:http://ustbhuangyi.github.io/better-scroll/doc/api.html
better-scroll 是一款重点解决移动端(已支持 PC)各种滚动场景需求的插件。它的核心是借鉴的 iscroll 的实现。
1、实现下拉刷新。
2、实现上拉加载数据
服务器渲染与Nuxt.js
Nuxt.js 是一个基于 Vue.js 的通用应用框架,利用 Vue开发服务端渲染的应用所需要的各种配置。解决vue不支持SEO优化的问题。
确保安装了npx(npx在NPM版本5.2.0默认安装了):
$ npx create-nuxt-app <项目名>
或者用yarn :
$ yarn create nuxt-app <项目名>
head配置:
head: {
title: process.env.npm_package_name || '',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1,maximum-scale=1.0,user-scalable=no' },
{
hid: 'description',
name: 'description',
content: process.env.npm_package_description || ''
},
{
name:"keywords",
content:"大码女装,好运买"
},
{
name:"description",
content:"好运买大码女装品牌网为胖mm精选优质大码女装品牌旗舰店,是您身边的专属衣橱,介绍各种流行的特大加肥加大时尚大码女装品牌,以及韩版大码女装,外贸大码女装,帮助胖妹妹尽快找到合适的服装。"
},
{
name:"format-detection",content:"telephone=no,email=no,date=no,address=no"
}
],
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
//引入手淘flexible.js
script: [{ src: './js/flexible.js', type: 'text/javascript', charset: 'utf-8'}]
},
全局CSS配置:
css: ['element-ui/lib/theme-chalk/index.css','./assets/css/common/public.css']
自定义插件配置:
plugins: [{src:'@/plugins/element-ui',ssr: true},'@/plugins/request'],
全局变量配置:
env: {
//ajax接口地址
baseUrl: process.env.NODE_ENV=='production'?"https://vueshop.glbuys.com/api":"/api",
//根目录
basePath:"/",
token:"1ec949a15fb709370f"
},
配置代理解决跨域问题:
proxy: {
'/api': {
target: 'https://vueshop.glbuys.com/api', // 目标接口域名
pathRewrite: {
'^/api': '', // 把 /api 替换成 空
changeOrigin: true // 表示是否跨域
}
}
},
远程服务器部署上线
阿里云服务器官网: https://www.alibabacloud.com
阿里云是阿里巴巴集团(纽交所代码:BABA)旗下的子公司。通过提供全面完善的全球云计算服务。
nginx下载地址:http://nginx.org/en/download.html
目前最流行最火的web服务器有:Apache、Nginx。
Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。
支持:php、nodejs、直播推流等。
使用nginx配置代理exproess服务器
1、启动express服务器
supervisor app.js
http://localhost:300
2、nginx配置反向代理
打开nginx.conf
server {
listen 1920;
server_name localhost;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Nginx-Proxy true;
proxy_set_header Connection "";
proxy_pass http://localhost:3003;
}
}