Vue的学习(一:基本使用到路由部分)

官网 https://cn.vuejs.org/

前言

封装 VS 库 VS 框架

封装通常指一小部分通用业务逻辑,多个封装形成一个模块或者文件,多个模块或者文件就发展成为或者框架,而插件是为库或者框架发布后做后期补充,可以有官网或者第三方提供的,有点外挂的意思,有时候一个模块就是一个文件,有时候一个文件里面有多个模块,把不同的文件按类别放置到不同的目录里,这个目录叫做,框架改变了编码思想,库只是个工具你用或者不用不会影响你的编码思想

编码思想
jquery你用或者不用,你的编码思想都会是面向事件,开发一款插件中间用到什么库不重要,你会面向对象开发,如果用vue你要做的就是面向数据

vue的基本使用

使用vue动态渲染数据

<div id="app">
	<!-- vue使用的mustache模板引擎 -->
    {{ message }}
</div>

引入vue.js之后

var app = new Vue({
	// el:'选择器'
    el:'#app',
    data:{
        message:"Hello Vue!"
    }
})

注意:多vue实例控制多片html,多实例不可嵌套,body和html不可作为被控制的dom根

data选项

vue 是一种配置型框架
初始化数据位置,元数据,是vue实例的一个实例属性,所接受的数据类型,number/string/boolean/array/json/undefined/null

注意:undefined和null不被渲染

数据绑定

插值表达式

{{数据名}} mustache语法 声明式渲染

指令

v-text="数据名"

v-html="数据" 非转义输出

属性

v-bind:html属性="数据" 属性值动态化
:html属性="数据" 简写

v-bind:[属性名]="数据" 属性名动态化

<div id="app">
    <!-- 插值 -->
    <h2>{{ text }}</h2>
    <!-- 指令 -->
    <div v-text="num">
    </div>
    <!-- 属性 -->
    <!-- 可简写 -->
    <!-- :html属性="数据" -->
    <!-- :[属性名]="数据" 属性名动态变化 -->
    <img :src="url" :[msg]="alt">
</div>
let app = new Vue({
    el:'#app',
    data:{
        text:"一些文本",
        num:2333,
        url:"https://cn.vuejs.org/images/logo.svg",
        msg:"alt",
        alt:"vue的图片"
    }
})

列表渲染

把数据指定到一些dom中去渲染,推荐操作的数据类型:变量数组、对象、字符、数字

<li v-for="值 in 数据">{{值}}</li>
<li v-for="值 of 数据">{{值}}</li>
<li v-for="(值,索引) in 数组">{{值}}/{{索引}}</li>
<li v-for="(对象,索引) in|of 数组">{{对象.key}}/{{索引}}</li>
<li v-for="(值,键) in 对象">
<li v-for="(数,索引) in 数字">
<li v-for="(单字符,索引) in 字符">

空数组,null,undefined不循环

找不到的数据就不渲染

<div id="app">
    <ul>
        <li v-for="data in arr">{{data}}</li>
    </ul>
    <ul>
        <li v-for="data of arr">{{data}}</li>
    </ul>
    <ul>
        <li v-for="(data,index) in arr">{{ data }} / {{ index }}</li>
    </ul>
    <ul>
        <li v-for="(object,index) in arr2">{{ object.id}} / {{ index }}</li>
    </ul>
    <ul>
        <li v-for="(value,index) in arr3">{{ value.name }} / {{ index }}
            <ul>
                <li v-for="(value,key) in value.children">{{ value.name }} / {{ key }}</li>
            </ul>
        </li>
    </ul>
</div>
let app = new Vue({
    el: "#app",
    data: {
        arr: ['aa', 'bb', 'cc'],
        arr2: [{
                id: 1,
                name: 'alex',
                age: 18
            },
            {
                id: 2,
                name: 'alex2',
                age: 28
            },
            {
                id: 3,
                name: 'alex3',
                age: 38
            },
        ],
        arr3: [{
                id: 1,
                name: 'alex',
                age: 18
            },
            {
                id: 2,
                name: 'alex2',
                age: 28,
                children: [{
                        id: 1,
                        name: 'ppp',
                        age: 18
                    },
                    {
                        id: 2,
                        name: 'ppp2',
                        age: 28
                    },
                    {
                        id: 3,
                        name: 'ppp3',
                        age: 38
                    },
                ]
            },
            {
                id: 3,
                name: 'alex3',
                age: 38
            },
        ],

        json: {
            a: 1,
            b: 2
        }
    }
})

条件渲染

一段dom可以根据数据有条件的渲染,使用指令v-show,或者v-if,对应的值是布尔

<div v-show="true">box1</div>
<div v-if="false">box2</div>

v-showv-if的区别

v-show=“布尔”v-if=“布尔”
区别操作css操作dom
场景适合频繁切换适合不频繁切换
性能消耗初始渲染消耗频繁切换消耗
<div id="app">
    <!-- v-if-xx的嵌套和执行顺序有关 -->
    <!-- 只渲染同级为true的,即if为false时候else为true -->
    <div v-if="station1">1</div>
    <div v-else>3</div>
    <div v-else-if="station2">2</div>
    <div v-show="station4">4</div>
</div>
let app = new Vue({
    el:"#app",
    data:{
        station1:false,
        station2:true,
        station4:false,
    }
})

事件绑定

vue通过v-on指令绑定事件,处理函数需要丢到一个methods选项当中去

<button v-on:不带on的源生事件名="方法"..
<button	@事件名="方法"	...
<button	@事件名="方法(参数)" .....
<button	@事件名="方法($event,参数)"	.....

事件名 不带on

new Vue({
  methods:{
    方法:function(ev,参数){业务}
    方法2(ev,参数){业务}
  }
})

ev 事件对象,参数可以有多个

注意:vue提供的选项的值如果是函数时,不可用箭头函数 , 会造成this丢失

<div id="app">
    <div v-show="station">
        一些内容,和显示隐藏关联
    </div>
    <div>{{ arr }}</div>
    <button v-on:click="show">显示隐藏</button>

    <button @click="add('d')">新增</button>

    <button @click="show1">看看箭头函数的this</button>
</div>
let app = new Vue({
    el:"#app",
    data:{
        station:true,
        arr:["a","b","c"]
    },
    methods:{
        // 这是vue虚拟的事件对象
        show:function(eve){
            // 不传参数,默认传递事件对象
            // console.log(eve);
            // console.log(this);
            // 当前点击的状态
            this.station = !this.station
        },
        add:function(val){
            this.arr.push(val);
        },  
        show1:()=>{
            // 箭头函数没有this,指向上一层的this,顶层的对象是window
            console.log(this)
            console.log(app);
        }
    }
})

双向绑定

视图控制数据,数据也可控制视图,可通过属性+事件绑定实现,也可以使用系统指令v-model,这个指令可以用在能生产数据的表单元素上

<div id="app">
    <!-- v-model同步修改value的值(数据控制视图),和其他的效果可以组成双向绑定 -->
    <input type="text" v-model="value">
    <input type="text" :value="value" @input="inputMsg">

    <div>{{value}}</div>
</div>
let app = new Vue({
    el: "#app",
    data: {
        value: "test"
    },
    methods: {
        inputMsg:function(eve) {
            // 获取当前输入框的值再赋值给值变量
            // v层修改m层,m层通过动态响应再重新渲染v
            this.value = eve.target.value
        }
    }
})

类和实例API

Vue 是个类,所以Vue.protoname,是类属性|静态属性,Vue.methodname()是类方法|静态方法,new Vue返回的是实例,所以vm.$protoname是实例属性,vm.$methodname()是实例方法,同时vue类内部的this指向的是实例vm,实例vm.$protoname对等vue选项的key

非响应式情况

情况要发生

  • 对数组使用了 非变异 (non-mutating method) 方法(返回的了新数组)
  • 修改数组的长度时
  • 修改数组索引上的值(根索引)
  • 给对象添加了不存在的属性

问题还是要解决

Vue.set(数组, index, value)

vm|this.$set(对象, key, value)

this.$forceUpdate() 强制刷新

this|vm.$mount(’#app’)

注意
不要修改数组的根键,不要修改数组的长度,数据一开始都要声明在data选项内部,不要对数组使用非变异的api

特殊的属性

key
key 的特殊 attribute 主要用在 Vue 的虚拟 DOM 算法

<div id="app">
    <ul>
        <!-- 数组的值和索引 -->
        <!-- 默认情况下 系统在分发数组的key的时候是以数组的索引为key的:key="index" -->
        <!-- 因为默认key是数组的索引,所有在操作数组第0个时就会产生数组塌陷,即删除了arr[0]但实际上arr[0]还存在,只不过此时的arr[0]是未删除的arr[1],又因为arr[0]还存在其对应的key还是存在,key在虚拟dom中对应的属性还是存在的 -->

        <!-- 为了解决这个问题需要主动去分发key -->
        <!-- 给指定循环的dom一个key 是数据的id,确保key唯一性,避免数据错乱导致的视图问题,同时提供性能优化 -->
        <!-- 这里的性能优化是通过唯一key直接找到目标,不会一层层去寻找 -->
        <li v-for="(val,index) in personList" :key="val.id">
            {{val.name}}
            <button @click="del(index)">删除</button>
            <button @click="change">改颜色</button>
        </li>
    </ul>
</div>
let app = new Vue({
    el: "#app",
    data: {
        personList: [{
                id: "1",
                name: "test01"
            },
            {
                id: "2",
                name: "test02"
            }
        ]
    },
    methods: {
        del(index) {
            this.personList.splice(index, 1)
        },
        // 事件对象是默认传的
        change(eve) {
            // 误操作,操作了虚拟dom
            // 虚拟dom会拷贝相同的dom,渲染的时候再次渲染不会重新创建
            eve.target.parentNode.style.background = "pink"
        }
    }
})

模板中js表达式的使用

Vue.js 提供了完全的 JavaScript 表达式支持。这些表达式会在所属 Vue 实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能包含单个表达式

插入数据的地方,可以出现表达式,但不是语句,如{{数据+表达式}} v-指令="数据+表达式"

<div id="app">
    <!-- 单个表达式 -->
    <!-- 算术表达式 -->
    <div>{{ num*10 }}</div>
    <!-- 三木运算 -->
    <div>{{ show? "yes" : "no"}}</div>
    <!-- 字符串的处理 -->
    <div>{{ str.split(" ").reverse().join(" ") }}</div>

    <!-- 在表达式中不能使用语句 -->
    <!-- <div>{{ if(true){return "some message"} }}</div> -->
</div>
let app = new Vue({
    el: "#app",
    data: {
        num: 10,
        show: false,
        str: "Hello Vue !"
    }
})

计算属性

是一个函数,所依赖的元数据变化时,会再次执行,平时会缓存,是响应式的,需要在模板中渲染才可调用

计算属性是基于响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数

语法

//定义
computed:{
    计算属性: function(){return 返回值}		
}

//使用
使用:	{{计算属性}} |  v-指令="计算属性" | :[计算属性]="计算属性"

computed VS method

methodcomputed
方法会每次调用基于它们的响应式依赖进行缓存的
一般性能高
{{methodname()}}{{computedname}}
适合强制执行和渲染适合做筛选
<div id="app">
    <div>{{ "使用计算属性处理逻辑: " + reversMsg }}</div>

    <h2>用计算属性做筛选</h2>
    <!-- 用v-model绑定data数据 -->
    <input type="text" v-model="intMsg">

    <ul>
        <!-- 遍历数据 -->
        <!-- 这里将函数当作属性 -->
        <!-- 因为这里是的函数体,没有执行,这个函数最终返回的是处理之后的结果也就是数组,所以这里能将函数当作属性处理 -->
        <li v-for="(val,index) of msgArrL">{{ val }}</li>
    </ul>
</div>
let app = new Vue({
    el: "#app",
    data: {
        msg: "Just Do It !",
        msgList: ["abc", "bcd", "ccc", "dbr"],
        intMsg: ""
    },
    // 计算属性
    computed: {
        // 翻转字符
        reversMsg: function () {
            return this.msg.split(" ").reverse().join(" ");
        },
        // 筛选字符
        // 方法简写
        msgArrL() {
            let result = [];
            // 遍历原数据
            this.msgList.forEach((val, index) => {
                // 查询输入框输入的数据
                if (val.includes(this.intMsg)) {
                    // 把数据添加到空数组中,最后将结果返回出去
                    result.push(val);
                }
            });
            return result;
        }
    }
})

属性检测(侦听器)

需要在数据变化时执行异步或开销较大的操作时,而计算属性是同步的,这个时候需要属性检测watch

定义一个选项

watch:{
  数据名:'method函数名'    //数据名==data的key
  数据名:函数体(new,old){}
  数据名:{
    handler:fn(new,old){},
    deep: true //深度检测
    immediate: true //首次运行
  }
}

注意:在变更 (不是替换) 对象或数组时,旧值将与新值相同,因为它们的引用指向同一个对象/数组。Vue 不会保留变更之前值的副本。

计算属性 VS 函数 VS 属性检测

计算属性函数属性检测
依赖模板调用且有返回值-×
是否缓存×
异步×
<div id="app">
    <input type="text" v-model="input">

    <ul>
        <li v-for="(val,index) in list">{{ val }}</li>
    </ul>
</div>
let app = new Vue({
    el: "#app",
    data: {
        list: [],
        input:''
    },
    watch: {
        input() {
            setTimeout(() => {
                let result = ['a', 'b', 'c']
                this.list = result;
            }, 1000)
        }
    }
});

样式操作(class与style绑定)

操作样式,就是属性绑定,只不过绑定的属性是class和style

绑定形式

<div v-bind:class="数据|属性|变量|表达式"></div>
<div :class="数据|属性|变量|表达式"></div>

<div v-bind:style="数据|属性|变量|表达式"></div>
<div :style="数据|属性|变量|表达式"></div>

属性值的类型支持

字符/对象 / 数组

<div class="active t1"></div>
<div :class="'active t1'"></div>
<div :class="{active:true,t1:false}"></div>
<div :style="[{css属性名:值},{'xx-xx-xx':值}]"></div>

示例

.Fblue{
    color: blue;
}
<div id="app">
    <!-- class绑定 -->
    <h1 :class="blue">class绑定</h1>
    <!-- style绑定 -->
    <h1 :style="yellow">style绑定</h1>
    <!-- 属性值类型支持 -->
    <h1 :style="[{background:'green'},{'font-size':'12px'}]">属性值类型支持:字符/对象/数组</h1>
</div>
let app = new Vue({
    el:"#app",
    data:{
        blue:"Fblue",
        yellow:"background-color: yellow;"
    }
});

指令

扩展了html语法功能,区别了普通的html属性,vue系统自带了指令,也可自定义指令来扩展,所有系统指令在官方文档的API处提供

其他系统指令

v-pre

保留字不编译,原样输出,跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译

v-cloak

防闪烁,模板没编译完,电脑配置差,有可能会看到{{}},体验不佳,用css先隐藏,之后再显示,包括被包裹的子元素

v-once

只渲染元素一次。随后的重新渲染被忽略,包括被包裹的子元素。这可以用于优化更新性能

<div id="app">

  <h3>系统指令</h3>

  <div>{{msg}}</div>

  <div v-text="msg"></div>

  <div>{{s}}</div>

  <div v-html="s"></div>

  <div>
    <h3>div事例</h3>
    <pre>
       <div>内容</div>
       function show(){
         
         ...
         
       }
     </pre>
  </div>

  <h3>mustatch语法</h3>

  <div v-pre>{{内容}}</div>

  <div v-once>{{msg}}</div>

  <div v-cloak>
    <div>
      {{12}}
      <ul>
        <li><a href="">{{'qq'}}</a></li>
      </ul>
    </div>
  </div>

</div>
let vm = new Vue({
  el: '#app',
  data: {
    msg: '数据1',
    s: '<strong>强壮</strong>'
  },
})
自定义指令

系统指令在不够用的情况下,考虑自定义,指令是个函数|对象,用来操作dom的, 里面的this 返回window

全局定义

Vue.directive('指令名',函数(el,binding){})

局部定义

new Vue({
	directives:{
    指令名	: function(el,binding){},//简写方式: bind + update
  	指令名(el,binding){},
    指令名:{
				inserted:fn(el,binding){}		//绑定指令的元素插入到父节点时调用  v-focus
				bind:fn	//指令第一次绑定到元素时调用	v-drag
				update:fn	//指令所在的元素的model层的数据,view有更新请求时
				componentUpdated:fn	//更新完成时
    }
  }
})
<div id="app">
    <h1 v-color>自定义全局指令默认值</h1>
    <h1 v-color="'pink'">自定义全局指令指定值</h1>
</div>
<div id="app1">
    <h1 v-color>自定义局部指令默认值</h1>
    <h1 v-color="'yellow'">自定义全局指令指定值</h1>
</div>
// 全局指令
// el:使用指令的dom元素 binding:对象,含有调用指令时传入的参数
Vue.directive("color", (el, binding) => {
    // 默认值设为蓝色
    el.style.background = binding.value || "blue";
});

// 局部指令的优先级比全局的优先级高
// 局部指令
let app1 = new Vue({
    el: "#app1",
    directives: {
        color: (el, binding) => {
            // 默认值设为红色
            el.style.background = binding.value || "red";
        }
    }

});
// 要选中dom才能使用vue渲染
let app = new Vue({
    el: "#app",
});
自定义指令–钩子函数(可选)

参考文档

一个指令定义对象可以提供如下几个钩子函数 (均为可选):

bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。

componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。

unbind:只调用一次,指令与元素解绑时调用。

实例:获取输入框焦点

<div id="app">
  <h3>自定义指令-钩子</h3>

  <input type="text" name="" id="">
  <input type="text" v-focus>
  <input type="text" name="" id="">

</div>
// 原生获取焦点的写法
// window.οnlοad=function(){
  // let oIpt = document.getElementById('ipt');
  // oIpt.οnclick=function(){}//鼠标触发
  // oIpt.click();//模拟点击
  // oIpt.focus()
// }

let vm = new Vue({
  el: '#app',
  data: {
    msg1: '数据1'
  },

  directives: {
    // 没有加钩子
    /* focus(el,binding){
      // console.log(1,el,binding,this);// this指向window
      el.focus()
    } */

    // 钩子函数可选
    focus: {
      // inserted函数:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)
      inserted(el, binding) {
        // console.log(1,el,binding,this);// this指向window
        el.focus()
      }
    }
  }
})

数据交互

向服务器发送ajax请求,抓取数据

解决方案

  • 自行通过XMLHttpRequest对象封装一个ajax
  • 使用第三方自带的ajax库,如:jquery ×
  • 把XMLHttpRequest,封装成一个promise
  • 使用js原生自带的promise语法糖 fetch
  • 使用第三方ajax封装成promise习惯的库,如:vue-resourceaxios

封装ajax到promise

function maxios(options) {
  
  return new Promise((resolve, reject) => {
    
    //-1  整理options
    options = options || {};
    options.data = options.data || {};
    options.timeout = options.timeout || 0;
    options.method = options.method || 'get';

    //0 整理data
    var arr = [];
    for (var key in options.data) {
      arr.push(key + '=' + encodeURIComponent(options.data[key]));
    }
    var str = arr.join('&');

    //1	创建ajax对象
    if (window.XMLHttpRequest) {
      var oAjax = new XMLHttpRequest(); //[object XMLHttpRequest]
    } else {
      var oAjax = new ActiveXObject('Microsoft.XMLHTTP')
    }

    if (options.method == 'get') {
      //2.
      oAjax.open('get', options.url + '?' + str, true);
      //3.
      oAjax.send();
    } else {
      //2.
      oAjax.open('post', options.url, true);
      //设置请求头
      oAjax.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
      //拼接好的地址栏
      oAjax.send(str); 
    }

    //3.5	超时
    if (options.timeout) {
      var timer = setTimeout(function() {
        alert('超时了');
        oAjax.abort(); //中断ajax请求	
      }, options.timeout);
    }

    //4.
    oAjax.onreadystatechange = function() { //当准备状态改变时
      if (oAjax.readyState == 4) { //成功失败都会有4
        clearTimeout(timer);
        if (oAjax.status >= 200 && oAjax.status < 300 || oAjax.status == 304) {
          resolve(JSON.parse(oAjax.responseText))
        } else {
          reject(oAjax.status)
        }
      }
    };
  })
}

axios

this.$http|axios({配置}).then(成功回调(res)).catch(失败回调(res))
this.$http|axios.get(url,{配置}).then(成功回调(res)).catch(失败回调(res))
this.$http|axios.post(url,data,{配置}).then(成功回调(res)).catch(失败回调(res))

配置:

url:“地址“
method: “ 提交姿势”
params:{} 地址栏携带的数据
data:{} 非地址栏携带数据

res: 响应体 数据是在res.data内部 vue-resource 返回的数据在res.body内部

访问本地json

// axios({ //第三方的axios
  this.$http({ //把axios绑定到Vue的原型
    url: './data/user.json'
  }).then(
    res => console.log('maxios res', res)
  ).catch(
    err => console.log('maxios err', err)
  )

读取php接口get

axios({
  url: 'http://localhost:80/php7/get.php',
  params: {//地址栏数据
    a: 11,
    b: 22
  }
}).then(
  res => this.list = res.data
)

跨域

前端和后端的工程文件不在同一个域,也会出现跨域,以下是解决方案

后端解决

部分接口允许

//node 要允许的接口内部 
res.setHeader('Access-Control-Allow-Origin', req.headers.origin)

//php端
header('Access-Control-Allow-Origin:*');

所有接口允许

//node端
let cors = require('cors');

app.use(cors({
  //允许所有前端域名
  "origin": ["http://localhost:8001","http://localhost:5000","http://localhost:8080"],  
  "credentials":true,//允许携带凭证
  "methods": "GET,HEAD,PUT,PATCH,POST,DELETE", //被允许的提交方式
  "allowedHeaders":['Content-Type','Authorization','token']//被允许的post方式的请求头
}));
前端解决

jsonp

浏览器装插件 正向代理

开发环境做代理(webpack,反向代理,客户端代理)

php接口的测试

1.搭建php环境

2.链接mysql数据库

3.测试接口(根据接口文档测试)

  • 地址栏地址的书写:http://localhost:服务器端口(没有修改apache端口默认为80)/xx.php文件?act=接口名(&参数=xxx)(()中为可选)
  • 示例添加一条数据:http://localhost:80/weibo.php?act=add&content=xxx

生命周期

每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会

官方文档
在这里插入图片描述
这个流程图就是vue整个的生命周期流程,我们能操作的是红色虚线箭头指向的阶段
根据这个流程图我们可以有两次绑定元素的机会:el和template,如果在这两个参数都不绑定的话html中的vue将不被选中渲染
最推荐的是在mounted中添加自己的代码,因为mounted已经将vue渲染完毕,如果进行修改的话vue的响应式系统会响应;
最不推荐的是在updated中添加自己的代码,在这里添加代码如果操作变化的数据会引起响应式系统一直在响应变化的数据,即一直在Mounted渲染,不会停止。

组件

组件是可复用的 Vue 实例,且带有一个名字
Vue根实例表示1个应用,一个应用有若干个组件拼装而成

使用组件

<组件名></组件名>
<组件名/>

定义组件

let 组件变量名= Vue.extend({
  template:'<div class="header">我是header组件</div>' //组件使用的模板
});

let 组件变量名={ template: 对应的模板的id };  //√

注册组件

//全局注册
Vue.component('组件名',组件变量名);

//局部注册
components:{ //选项
  组件名:组件变量名	//√
}

案例

<div id="app">
    <!-- 使用组件 -->
    <v-head></v-head>
    <v-footer></v-footer>
</div>

<!-- 外部模板定义方式1 -->
<template id="tHead">
    <div class="Head">head</div>
</template>

<!-- 外部模板定义方式2 -->
<script type="x-template" id="tFoot">
    <div class="footer">footer</div>
</script>
// 定义组件
let VHead = {
    // 用template绑定相应的外部模板
    template: "#tHead"
}

let VFoot = {
    template: "#tFoot"
}

// 注册组件(将组件名和组件变量名关联)
// 全局注册
Vue.component("v-head", VHead)

let app = new Vue({
    el: "#app",
    // 局部注册
    components: {
        "v-footer": VFoot
    }
});

组件数据data
一个组件的 data 选项必须是一个函数,且要有返回object,因此每个实例可以维护一份被返回对象的独立的拷贝,否则组件复用时,数据相互影响。这是因为js本身的特性带来的,跟vue本身设计无关,也就是说组件的作用域是独立的

组件模板

外部模板,字符模板(行间模板|inline-template)

//=========外部模板========

//模板定义1
<template id="id名">
	<div>...</div>
</tempalte>

//模板定义2
<script type="x-template" id='id名'></script>

<script>
let 组件变量名= {
  template:'#id名' //组件使用的外部模板
};
</script>


//=========字符模板========
<script>
let 组件变量名= Vue.extend({
  template:'<div>...</div>'
});
</script>

组件的注意事项

  • 组件名不可和html同名

  • 组件没有el选项,只有根实例存在el

  • 组件的模板有且只有一个根元素

  • 组件的data是个函数,需要返回对象

书写风格

  • 组件变量名: 大驼峰 XxxXxx
  • 组件名: xx-xx | 大驼峰|app-header-nav

单文件组件

xx.vue,内部组成(script + template + style)

案例

<div id="app">
    <!-- 使用组件 -->
    <v-head></v-head>
    <v-foot></v-foot>    
</div>

<!-- 外部模板 -->
<!-- 不要将外部模板放入要vue渲染的区域,否则会再次渲染外部模板里的内容,造成意外的错误 -->
<template id="vhead">
    <!-- 组件的模板有且只有一个根元素,如果不加最外一层盒子的话,vue就认为img和ul都为根元素 -->
    <!-- 解决方法一:都显示 -->
    <!-- 最外层套一层盒子 -->
    <div class="vhead">
        <img :src="url" alt=""/>
        <ul>
            <li v-for="(val,index) in List">{{ val }}</li>
        </ul>
    </div>
</template>

<style>
    .vhead{
        background-color: cadetblue;
    }
</style>
<!-- 外部模板 -->
<script type="x-template" id="vfoot">
    <!-- 只能有一个根元素 -->
    <!-- 解决方法二:只显示一个 -->
    <div class="foot1" v-if="true">{{ msg }}</div>
    <div class="foot2" v-else>{{ msg }}</div>
</script>
<script>
    // 定义组件
    let VHead = {
        template:"#vhead",
        // 组件的数据
        // 简写
        // 组件并不渲染真实dom,是交给根实例去渲染,所以需要在组件中的data是一个函数且需要return
        data(){
            return {
                url:"https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png",
                List:[1,2]
            }
        }
    }

    let VFoot ={
        template:"#vfoot",
        data(){
            return {
                msg:"only one foot"
            }
        }
    }

    let app = new Vue({
        el:"#app",
        // 局部注册组件
        components: {
            "v-head":VHead,
            "v-foot":VFoot
        }
    })

</script>

看完这个例子再回去看组件的注意事项,书写风格,单文件组成,会有具体的理解

工程化开发环境(Vue CLI)

通过官方脚手架,手动搭建模块化,工程化,自动化开发环境
官方文档https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create

安装vue,项目配置及启动项目

// 安装
// 1.查看是否已经安装
vue -V  //查看版本 
// 提示不是命令则未安装
//非3.x/4.x/5.x 时需要先卸载
npm unstall vue-cli -g  

// 2.安装vue
npm install -g @vue/cli  //装3/4/5
npm install -g @vue/cli-init //桥接2  

//3.测试
vue -V
// 此时vue -V说还不是命令则需要配置vue的环境变量

// 创建项目的配置
// 1.在要创建项目的目录下初始开发环境
vue create 项目名
// 2. 选择自定义
// 3. 选中 babel-vue2
// 4. 选择各个包的配置文件不都放置在package.json,即每个包的配置文件都独立显示
// 5.是否保存此次设置,可以选择y/n,y的话起个名字下次创建项目选择这个名字就能生成项目

// 启动项目
// 运行服务
npm run serve
// 若能正常启动,且通过默认端口访问到则说明项目启动成功
// 浏览器不能识别vue文件,只能识别html,css,js等文件,所以在运行服务的时候这个服务会将vue等文件打包之后实时发送到浏览器,这个包是存在运行内存中的,并不会直接生成打包之后的文件,这些打包文件随着服务的关闭而销毁

// 打包项目
// 正式上线
npm run build

//问题汇总。
1. vue : 无法加载文件 ...,因为在此系统上禁止运行脚本

解决: https://blog.csdn.net/wqnmlgbsz/article/details/100654258
hbx内部设置当前策略: Set-ExecutionPolicy -Scope CurrentUser -> RemoteSigned


2. vue-cli安装失败
分析:安装全局包 登录账号要是admin  |  管理运行 cmd | powersheel ,node要全量安装
解决:
   a 换手机5g ,关防火墙,重装
   b node要全量安装
   c hbx自动创建
   d npx vue create 目录
   e copy 被人 创建好的项目

HbuildX自动搭建模块化,工程化,自动化开发环境

工作区-》右键-》创建项目-》普通项目-》vue-cli项目

//测试
工作区-》右键-》外部命令->npm run serve

环境分析

通过脚手架生成的目录/文件
默认生成的是单页面(public->index.html)
通过脚手架生成的目录/文件

  • bable用来做优雅降级
  • package.json
    • server:服务。 将src文件夹(开发环境)下的App.vue放入到运行内存中编译成dist文件夹(此时dist文件夹不存在,因为浏览器只能识别html,css,js等文件)里的文件,让浏览器去浏览,随着服务的停止而销毁运行内存里的打包好的文件
    • build:打包。因为浏览器不识别编写的vue文件,所以需要打包来根据src文件夹里面的内容生成dist文件夹(没有主动打包不存在)里面的内容,浏览器才能识别
  • public
    • 包含静态资源
  • src
    • 包含所有开发的代码
  • dist
    • 包含所有打包好的文件
  • main.js
    • 根实例

main.js的解释

Vue.config.productionTip = false//设置为 false 以阻止 vue 在启动时生成生产提示。
new Vue({
  
  // render: h => h(App)的推导过程
  
  // 渲染render
  /* render: function(createElement){
    //业务  创建vdom
    // createElement(vdom|组件)
    
    // return createElement(App)
    return createElement('div','box')
  }, */
  
  /* render: (createElement)=>{
    //业务  创建vdom
    // createElement(vdom|组件)
    
    // return createElement(App)
    return createElement('div','box')
  }, */
  
  // render: createElement=>createElement('div','box'),
  // render: h=>h('div','box'),
  render: h=>h(App),
}).$mount('#app')
}

浏览器访问项目的顺序

浏览器浏览文件的是public下的index.html,这个脚手架会把所有vue渲染的东西添加到<div id ="app"> </div>内,然后再找到根实例main.js,main.js会打开App.vue,再找到子组件

es6模块化

输入

import a from './mod/a.js'  //输入 默认输出 的任意类型
import {对外变量,a,b,c} from './mod/a.js'  //输入批量输出的 属性,需要一个对象包装
import * as a from './mod/a.js'  //输入批量输出的 属性,包装到一个对象里面使用

import规则

  • import引入一个依赖包,不需要相对路径。如:import app from ‘app’;
    • 在node_modules 中,根据包名,找对应的vue文件夹
  • import 引入一个自己写的js文件,是需要相对路径的。如:import app from ‘./app.js’;

输出

//批量输出  any  导出多次
export const 对外变量 = 模块内部的变量
export {a,b,c} //批量输出模块内部的属性

//默认输出  any   只能导出一次
export default any

css 规则

style-loader 插入到style标签,style标签多了选择器冲突问题就出来了,解决方案如下

/* 方案1:	命名空间 推荐BEM命名风格*/
/*B__E--M  block 区域块  element 元素  midiler 描述|状态 */
.search{}
.search__input{}
.search__input--focus{}
.search__input--change{}
.search__button{}

//  B__E--M
//  b-b-b__e-e-e--m-m-m
<!--方案2 css模块化 -->
<template>
	<div :class="$style.box">
    ...
  </div>
</template>

<script>
export default {
  mounted(){
    this.$style //返回一个对象 {选择器:'混淆后的选择器',xx:oo}
  }
}
</script>

<style module>
  /* 注意: 选择器名(支持 id,类,或者id、类开头其他选择器) */
  .box{}
  #box2{}
  .box3 div{}
  #box3 div{}
</style>
<!--方案3 独立样式作用域-->
<template></template>
<script></script>
<style scoped></style>

编写vue程序的过程

根据上面分析访问程序的顺序
在App.vue中的template中使用子组件,在export default中注册引入的子组件;子组件中的template定义子组件,在export default中写数据

组件通讯基础

父子(props)单向数据流

官方文档

父组件通过属性绑定,子组件通过选项props接收,props是响应式的,props完成单向下行绑定

父传

< :自定义属性="父数据"></..>

子收

props:['自定义属性']
props:{自定义属性:{type/default/required/validator...}}

<div>
  {{自定义属性}}
</div>

注意:

​ props是只读的,不推荐改

props命名:

​ props: [‘postTitle’]

特殊情况

数据的深浅拷贝

在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态

子父

通过自定义事件实现,给子组件绑定自定义事件,子组件触发自定义事件时传递,事件函数是父组件方法,父方法负责接收

父绑定父接收

<template>
	..
	< @自定义事件="父方法"></..>
	..
</template>
<script>
export default {
  methods:{
    父方法(接受数据){处理}
  }
}
</script>

子触发子传递

<script>
	this.$emit('自定义事件',.数据名)
</script>

app.vue

// template模板只包裹一段元素,本身不会被渲染
<template>
  <div id="app">
    <!-- 自定义属性="数据" -->
    <v-child1 :Tochile1="Tochile1Name" :data2To1="getChild2Date"></v-child1>
    <!-- @toChild2是子组件的自定义事件 -->
    <!-- getChild2是父方法 -->
    <v-chiled2 @toChild2="getChild2"></v-chiled2>
    <div id="mainApp">
      {{ "这是从chiled2接收的数据 " + getChild2Date }}
    </div>
  </div>
</template>

<script>
import chiled1 from "./components/chiled1.vue";
import chiled2 from "./components/chiled2.vue";

export default {
  components: {
    "v-child1": chiled1,
    "v-chiled2": chiled2,
  },

  // 组件并不渲染真实dom,是交给根实例去渲染,所以需要在组件中的data是一个函数且需要return
  data() {
    return {
      // 传递给chiled1的数据
      Tochile1Name: "Tochile1",

      // 父要准备响应式的数据位,才能响应式显示从子接收到的数据
      // 接收chiled2传过来的数据
      getChild2Date: "-",

    };
  },
  methods: {
    // 接收第二个子传过来的数据
    getChild2(data2) {
      // 将chuled2传过来的数据赋给原来准备好的数据位
      this.getChild2Date = data2;
    },
  },
};
</script>

chiled1.vue

<template>
  <div id="child1">
    {{ msg1 }}
    <div id="parent">
      {{ Tochile1 }}
      <button @click="change">
        修改父组件传过来的值{{
          Tochile1
        }}(不建议修改,虽然能成功但是控制台会报错)
      </button>
      <br />
      {{ "这是从chiled2传给app的,app再传给chiled1" + data2To1 }}
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return { msg1: "child1" };
  },
  // 应该只读
  props: {
    Tochile1: {
      // vue封装了原生的所有的类型
      type: String,
    },
    data2To1: {
      type: String,
    },
  },
  // 不建议修改
  methods: {
    change() {
      this.Tochile1 = "changeAfterTochile1";
    },
  },
};
</script>

chiled2.vue

<template>
    <div id="chiled2" >
        <h2>{{ msg2 }}</h2>
    </div>
</template>

<script>
export default ({
    data(){
        return{
            msg2:"This is chiled2",
            data2:"chiled2 to Parent'data"
        }
    },
    // 钩子函数
    // 渲染完成时将数据传给父
    mounted () {
        // 父组件的自定义事件,要传递的数据
        this.$emit('toChild2', this.data2);
    }
})
</script>

集中管理($root)

把数据存到根实例的data选项,其他组件直接修改或者使用

定义

new Vue({
  data:{a:1}
})

使用

//组件内部
this // 组件本身
this.$root // vm
this.xx //组件内部数据
this.$root.a //根实例数据

this.$root.proname=value  //修改

vm.xx  //其他模块需要输入实例化对象vm

main.js 根实例定义

new Vue({
  render: h => h(App),
  data:{
    data1:"根实例数据1",
    data2:"根实例数据2",
    data3:"根实例数据3"
  }
}).$mount('#app')

模块内调用

data() {
    return {
      appData:this.$root.data1
    };
  },
{{ "chiled1使用root数据,this可以省略,因为这个组件在根实例下,this代表组件本身;" + $root.data2 }}

内容分发(插槽)

组件调用时,给其内部插入内容,如:<组件>内容</组件>,又叫:插槽投射,插入到组件内容的可以是dom或组件

使用组件
<组件>123</组件>
<组件><div>123</div></组件>
<组件><组件></组件></组件>

<组件名><元素 slot="槽名">...
<组件名><template #槽名>..
<组件名><template slot="槽名">...
<组件名><template v-slot:槽名>...
组件内部:	
<slot>匿名</slot>
<slot name=槽名></slot>

父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译

使用组件

<template>
  <div class="app">
    <!-- 子组件 -->
    <!-- 没有对应的插槽,就只渲染的内容 -->
    <v-header> </v-header>

    <hr />

    <v-header>
      <!-- 第一种使用插槽的方式,已废弃 -->
      <!-- 只要绑定插槽名即可,渲染顺序由组件内容决定 -->
      <a href="" slot="more">更多1</a>
      <h3 slot="title">OA1</h3>
    </v-header>

    <hr />

    <v-header>
      <!-- 另外一种调用插槽的方式,已废弃 -->
      <template slot="title">
        <!-- 没有插槽则插槽内的内容不会被渲染 -->
        <h3>OA2</h3>
        <h3>OA2</h3>
      </template>

      <!-- 另外一种调用插槽的方式,已废弃 -->
      <template #more>
        <a href="#">更多2</a>
      </template>
    </v-header>

    <hr />

    <v-header>
      <!-- 最新使用插槽的方式 -->
      <template v-slot:title>
        <h3>OA3</h3>
        <h3>OA3</h3>
        <h3>OA3</h3>
      </template>
      <template v-slot:more>
        <a href="#">v-slot:more</a>
      </template>
    </v-header>
  </div>
</template>

<script>
import Header from "./components/chiled1.vue";

export default {
  components: {
    //局部注册
    "v-header": Header,
  },
};
</script>

组件内部

<template>
  <div class="header">
    <!-- 插槽 -->
    <slot name="title"></slot>
    <nav>
      <a href="#">xxx</a>
      <a href="#">xxx</a>
      <a href="#">xxx</a>
      <a href="#">xxx</a>
    </nav>
    <!-- 插槽 -->
    <slot name="more"></slot>
  </div>
</template>

特殊的属性ref

尽管存在 prop 和事件,有的时候仍可能需要在 JavaScript 里直接访问一个子组件。为了达到这个目的,可以通过 ref 这个 attribute 为子组件赋予一个 ID 引用

引用元素(dom,组件<类,函数>)
官方链接1(api)
官方链接2(用法)

//父 template
<子 ref="自定义子名称"></..>
<div ref="自定义子名称">
  
</div>

//父 script
 this.$refs.自定义子名称.数据名
 this.$refs.自定义子名称.方法()

$refs 只会在组件渲染完成之后生效,并且它们不是响应式的,避免在模板或计算属性中访问 $refs

<template>
  <div class="app">
    <div ref="box1">box1</div>
    <div ref="box2">box2</div>
  </div>
</template>

<script>
export default {
  // $refs 只会在组件渲染完成之后生效,并且它们不是响应式的。这仅作为一个用于直接操作子组件的“逃生舱”——你应该避免在模板或计算属性中访问 $refs。
  mounted() {
    console.log("mounted", this.$refs);
    // 在普通的 DOM 元素上使用,引用指向的就是 DOM 元素
    this.$refs.box1.style.background = "red"; //dom  图形 媒体
  },
};
</script>

第三方组件的使用

使用一些别人开发好的单组件、组件库、插件、ui库,来提高项目开发进度,组件库通常是某公司开发并开源出来,获取方式可以通过npm、github查询,或者百度vue组件库,这里有一些推荐1推荐2

第三方单组件的使用方式

//安装
npm i vue-swipe -D  安装

//引入
import './node_modules/vue-swipe/dist/vue-swipe.css'; 引入样式
import { Swipe, SwipeItem } from 'vue-swipe'; 引入组件

Vue.component('swipe', Swipe);    //注册安装到全局
Vue.component('swipe-item', SwipeItem);

//注册到选项components 私有使用   自写样式避免与vue-swiper.css冲突  scoped

例子:vue轮播图插件vue-swipe的使用
文档:https://www.npmjs.com/package/vue-swipe
根据文档说明使用

<template>
  <div class="app">
    <!-- 自定义属性auto默认速度3000,这里改为1000 -->
    <!-- 自定义属性showIndicators默认true开启分页器小点,这里改为false -->
    <swipe class="my-swipe" :auto="1000" :showIndicators="false">
      <swipe-item class="slide1">1</swipe-item>
      <swipe-item class="slide2">2</swipe-item>
      <swipe-item class="slide3">3</swipe-item>
    </swipe>
  </div>
</template>

<script>
// 导入
require("vue-swipe/dist/vue-swipe.css");
import { Swipe, SwipeItem } from "vue-swipe";

export default {
  components: {
    // 注册到局部
    // 组件名:组件变量名
    swipe: Swipe,
    // "swipeItem": SwipeItem,
    // 编译的时候会组件名的大驼峰转换成xx-xx,上面的这种写法完全等同于下面这种写法
    // swipeItem === swipe-item  // true
    "swipe-item": SwipeItem,
  },
};
</script>

<style>
.my-swipe {
  height: 200px;
  color: #fff;
  font-size: 30px;
  text-align: center;
}

.slide1 {
  background-color: #0089dc;
  color: #fff;
}

.slide2 {
  background-color: #ffd705;
  color: #000;
}

.slide3 {
  background-color: #ff2d4b;
  color: #fff;
}
</style>
dom组件库类别

pc端、后台管理

  • element-ui 饿了么 √ (在vue用的比较多)
  • iview 个人
  • ant design 蚂蚁金服 (在react用的比较多)

移动端、客户端

  • vant 有赞 电商 √
  • mint-ui 饿了么
  • vue-material
  • muse-ui
  • VUX
  • cube-ui
  • vonic
  • Vue-Carbon
  • YDUI

通用

  • bootstrap5/4
  • ameizi

路由

官方文档
用来SPA (single page application 单页面应用),页面跳转

单页VS多页

页面模式多页面模式(MPA Multi-page Application)单页面模式(SPA Single-page Application)
页面组成多个完整页面, 例如page1.html、page2.html等由一个初始页面和多个页面模块组成, 例如:index.html
公共文件加载跳转页面前后,js/css/img等公用文件重新加载js/css/img等公用文件只在加载初始页面时加载,更换页面内容前后无需重新加载
页面跳转/内容更新页面通过window.location.href = "./page2.html"跳转通过使用js方法,append/remove或者show/hide等方式来进行页面内容的更换
数据的传递可以使用路径携带数据传递的方式,例如:http://index.html?account=“123”&password=123456"",或者localstorage、cookie等存储方式直接通过参数传递,或者全局变量的方式进行,因为都是在一个页面的脚本环境下
用户体验如果页面加载的文件相对较大(多),页面切换加载会很慢页面片段间切换较快,用户体验好,因为初次已经加载好相关文件。但是初次加载页面时需要调整优化,因为加载文件较多
场景适用于高度追求高度支持搜索引擎的应用高要求的体验度,追求界面流畅的应用
转场动画不容易实现容易实现

单页面模式:相对比较有优势,无论在用户体验还是页面切换的数据传递、页面切换动画,都可以有比较大的操作空间 多页面模式:比较适用于页面跳转较少,数据传递较少的项目中开发,否则使用cookie,localstorage进行数据传递,是一件很可怕而又不稳定的无奈选择

基础使用

安装引入注册

npm i vue-router -S

// src/main.js
import router from './plugins/router.js'
new Vue({
  router
})

配置路由

// src/plugins/router.js
import Vue from 'vue'

//1. 引入路由包
import VueRouter from 'vue-router'

//2. 安装插件包到Vue上, 
Vue.use(VueRouter);

//3. 路由配置
let routes = [
  {path: '/home',component: Home}, //route  一条路由的配置
]

//4.路由实例
let router = new VueRouter({ //插件路由对象
  // routes:routes
  routes,
});

//5.导出路由实例,让他去控制vue根
export default router

一些注意事项

  • vue.use安装插件功能,包含注册功能

  • 路由:"/"是所有的/xx的父

  • 声明式跳转都是调用了$router,添加历史记录(原生history)

  • 如何把下一级的展示区显示在上一级级里面?
    1.path:在上一级里面不再定义children,再定义一个下一级,如:path:‘二级/三级’
    2.下一级里的链接对应这个定义的,同时下一级级不再定义展示区,不定义展示区的话就会寻找上一级展示区default,达到我们想要的效果

文件目录层级
路由
main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
  // 路由控制根
  router,
  
  render: h => h(App)
}).$mount('#app')

router/index.js

// 默认导入的是node_modules下的包
import Vue from 'vue'
import VueRouter from 'vue-router'
// 导入自己写的文件
import Home from '../views/Home.vue'
// vue.use安装插件功能,包含注册功能
Vue.use(VueRouter)
// 配置路由
// 同级路由自上向下匹配
// 所以把重定向和404放到最后
// 路由:"/"是所有的/xx的父
const routes = [{
    // 路径
    path: '/home',
    // 路由名,可通过属性绑定形式:to="{name:'Home'}"实现跳转
    name: 'Home',
    // 组件
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: () => import('../views/About.vue')
  },
  {
    path: '/more',
    // 当某个路由有子级路由的时候,这时候父级路由需要一个默认的路由,所以父级路由不能定义name属性
    // name: 'More',
    component: () => import('../views/More.vue'),
    // 子路由
    // 子路径直接写,前面会加上父路径
    // 类型类似json,[{}]
    children: [{
        path: 'more1',
        name: 'More1',
        component: () => import('../views/more/more1.vue')
      },
      {
        path: 'more2',
        name: 'More2',
        component: () => import('../views/more/more2.vue')
      },
      // more2的三级标题
      // 在二级里面不再定义children
      // 直接定义下一级
      {
        path: 'more2/2',
        name: 'More2-2',
        component: () => import('../views/more/default.vue')
      },
      {
        path: 'more3',
        name: 'More3',
        component: () => import('../views/more/more3.vue')
      },
      // 子路由的默认页
      {
        path: '',
        redirect: 'more1'
      }
    ]
  },
  {
    // 重定向
    // 默认页
    path: '/',
    // 配置型跳转
    redirect: '/home'
  },
  {
    // 404页面
    // paht全部匹配,放在最后,以上path都找不到就跳转404
    path: '*',
    name: 'NotFound',
    component: () => import('../views/NotFound.vue')
  }
]
// 实例化路由
const router = new VueRouter({
  // routes:routes
  routes,
  // mode:'hash'// 默认哈希模式   原理:location.href
  mode: 'history' // 历史记录   原理:history.pushState
})
// 导出路由实例,控制vue的根
export default router

App.vue

<template>
  <div id="app">
    <div id="nav">
      <!-- 声明式跳转 -->
      <!-- 声明式跳转都是调用了$router,添加历史记录(原生history) -->
      <!-- 可以自定义激活样式,当模糊匹配和严格匹配分开也能同时激活父元素和子元素 -->
      <router-link to="/home">Home</router-link> |
      <router-link to="/about">About</router-link> |
      <router-link to="/more">More</router-link>
    </div>
    <!-- 当前选中元素的展示区 -->
    <router-view/>
  </div>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

#nav {
  padding: 30px;
}

#nav a {
  font-weight: bold;
  color: #2c3e50;
}
/* 严格匹配 */
/* 当前元素选中的样式 */
#nav a.router-link-exact-active {
  color: #42b983;
}
/* 模糊匹配 */
/* 父元素选中 */
.router-link-active{
  background: #68829c;
}
</style>

more2.vue

<template>
  <div id="more2">
    <h1>This is more2!!</h1>
    <div id="nav">
      <!-- 对应path -->
      <router-link to="/more/more2/2">more2-1</router-link>|
      <router-link to="/more/more2/2">more2-2</router-link>|
      <button @click="go">more2-3编程式跳转</button>
    </div>
    <!-- 不再定义展示区,会寻找上一级展示区default -->
  </div>
</template>
<script>
export default {
  methods: {
    go() {
      // console.log(this.$router);

      // 字符串
      // this.$router.push('/more/more2/2')

      // 对象
      // this.$router.push({ path: "/more/more2/2" });

      // 命名的路由,可在vue插件中查看params中查看参数
      // this.$router.push({ name: "More2-2", params: { userId: "123" } });

      // 带查询参数,变成 /2?plan=private
      this.$router.push({ path: "/more/more2/2", query: { plan: "private" } });
    },
  },
};
</script>
路由守卫

官方文档

全局守卫

// src/plugins/router.js

//前置
router.beforeEach((to, from, next) => {
  
  //	to: 目标路由 $route
  //	from: 当前路由 $route
  
  // next() 跳转  一定要调用
  next(false);//走不了
  next(true);//走你
  next('/login')//走哪
  next({path:'/detail/2',params:{},query:{}})//带些参数
  
  // 守卫业务
  if(to.path=='/login' || to.path=='/reg' || to.path=='/register'){
    //判断是不是登录了
    //axios请求 携带 token 
    next()
  }else{
    next('/login');
  }
  
})

//后置
router.afterEach((to,from)=>{
  //全局后置守卫业务
  // 如清理数据,关闭服务等
})

路由独享守卫

// src/plugins/router.js -> routes
{
  path: '/user',
  component: User,
  beforeEnter: (to,from,next)=>{ //路由独享守卫 前置 
    console.log('路由独享守卫');
    if(Math.random()<.5){
      next()
    }else{
      next('/login')
    }
  }
 },

注意路由独享,没有后置
注意 beforeRouteEnter 是支持给 next 传递回调的唯一守卫。对于 beforeRouteUpdate 和 beforeRouteLeave 来说,this 已经可用了,所以不支持传递回调,因为没有必要了。
可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。

beforeRouteEnter (to, from, next) {
  next(vm => {
    // 通过 `vm` 访问组件实例
  })
}

组件内部守卫

//组件内部钩子
beforeRouteEnter (to, from, next) {//前置   运行在beforeCreate 之前
  // 不!能!获取组件实例 `this`
  // 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
  // 在当前路由改变,但是该组件被复用时调用
  // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
  // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
  // 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {//后置  运行在beforeDestory之前
  // 导航离开该组件的对应路由时调用
  // 可以访问组件实例 `this`
}
路由元信息

定义路由的时候配置 meta 字段

//src/plugins/router.js
{
  path: '/home',
  component: Home,
  meta: { requiresAuth: true }
}

访问 meta 字段

this.$route.meta
to.meta from.meta
滚动行为

SPA是单页面,只有一个滚动条,路由跳转时滚动条会影响到元素位置,使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样

对于所有路由导航,简单地让页面滚动到顶部

// src/plugins/router.js
const router = new VueRouter({
  scrollBehavior (to, from, savedPosition) {
    //计算位置
    return { x: 0, y: 0 }
  }
})

本篇结束,下一篇:Vue的学习(二:路由+)

因为内容太多,csdn的富文本编辑器渲染性能下降,无奈只能写续篇

文章地址
https://blog.csdn.net/weixin_44902117/article/details/121116657

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值