vue笔记

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;
   }
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
posted @ 2019-09-28 12:15  文杰_豆豆  阅读(439)  评论(0编辑  收藏  举报