Vue 学习笔记

Vue学习笔记


一、介绍

1.官网地址

Vue官网地址-----------------------https://cn.vuejs.org/

Vue3官网地址---------------------https://v3.cn.vuejs.org/

2.Vue介绍

​ Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

3.兼容性

Vue 不支持 IE8 及以下版本,因为 Vue 使用了 IE8 无法模拟的 ECMAScript 5 特性。但它支持所有兼容 ECMAScript 5 的浏览器。

4.Vue Devtools

正谷歌浏览器应用商店搜索Vue Devtools,安装即可

5.命令行工具 (CLI)

参考地址:https://cli.vuejs.org/zh/
Vue CLI 需要 Node.js 8.9 或更高版本 (推荐 8.11.0+)。你可以使用 nvm 或 nvm-windows 在同一台电脑中管理多个 Node 版本。
安装环境:

npm install -g @vue/cli

6.创建一个项目

vue create 项目名称

启动项目:在项目的根目录下运行:

npm run serve

7.VSCode的插件 Vetur

node_modules 是该项目的依赖包

public 是静态资源文件夹

src 是项目代码

.git文件是git的配置代码

babel.config.jsbabel-loader的配置文件

package.json 是项目依赖文件

README.md 是项目的描述文件

8.生命周期函数

实例生命周期钩子:生命周期函数会随着我们对程序理解越深,可参考价值越高

export default {
  name: "",
  beforeCreate() {
    //   初始化操作
    console.log("组件创建之前:beforeCreate");
  },
  created() {
    //   初始化操作
    console.log("组件创建之后:beforeCreate");
  },
  beforeMount() {
    //   判断组件渲染之前要做的额外的事
    console.log("组件渲染之前:beforeMount");
  },
  mounted() {
    //   网络请求 or 操作DOM
    console.log("组件渲染之后:mounted");
  },
  beforeUpdate() {
    console.log("数据更新之前:beforeUpdate");
  },
  updated() {
    console.log("数据更新之后:updated");
  },
  beforeDestory() {
    //   将组件中需要清除的在次函数中清除
    //   定时器、持续事件、组件数据清空、清除未完成的网络请求
    console.log("组件销毁之前:beforeDestory");
  },
  destoryed() {
    console.log("组件销毁之后:destoryed");
  },
};

Vue 实例生命周期

二、知识点1

详细源码在01VueBase/vue-demo

1.模板语法

<template>
<div class="hello">
 <strong>{{message}}</strong>
 <p>{{1+1}}</p>   <!-- 2 -->
 <p>{{num+1}}</p><!-- 11 -->
 <p>{{10>20?'真的':'假的'}}</p><!-- 假的 -->
 <p>{{message.split('').reverse().join('')}}</p><!-- euv olleh -->
 <p v-html="vHtml"></p>
 <p v-bind:class="bindClass">动态属性</p>
 <a v-bind:href="Href">b站主页</a>
 <p v-bind:attr="attr">自定义属性</p>
 <p class="size" v-bind:class="bindClass">动态属性和静态属性不冲突</p>
</div>
</template>

<script>
export default {
// 函数
data(){
  return {
    message:'hello vue',
    num:10,
    name:['jdk','jre'],
    vHtml:'<strong>我是属性绑定</strong>',
    bindClass:'text-2',//动态赋值
    Href:'http://bilibili.com',
    attr:'100010'
    }
}
}
</script>
<style scoped>
.text-1{
color: red;
}
.text-2{
color: green;
}
.size{
font-size: 24px;
}
</style>

注意事项:模板中可以放语法,单个表达式语法

  1. 文本:`{{  }}`
  2. 原始HTML:`v-html`
  3. 动态属性绑定:`v-bind:`

2. CSS作用域

scoped:作用域 在Vue的文件中,也有css的作用域,增加scoped属性,样式只能在当前组件中生效

3. 条件渲染

v-if
v-else
v-show
v-ifv-show的区别:

v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
​ 相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
​ 一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用v-if较好。

4. 列表渲染

v-for
v-forv-if 一同使用:注意我们不推荐在同一元素上使用 v-if 和 v-for

  1. 渲染数组(itme,index)
  2. 渲染对象(value,name,index)

5. 事件处理

v-on:click="函数"

  1. 事件的实现

  2. 事件传递参数

  3. 事件修饰符

  4. 缩写::key @click

    v-bind:key -> :key    
    v-on:click -> @click
    

6. 表单输入绑定

  1. v-model指令

  2. 修饰符 .lazy .number .trim

    常见面试题:说一说双向数据绑定的原理!!!

7. 数组更新检测

7.1 数组方法的特异性

  1. 改变元素组(会引起视图更新) push() pop() shift() unshift() splice() sort() reverse()

  2. 不改变元素数组,而是生成新的数组(不会引起视图更新) filter()concat()slice()

7.2 对象的更新检测:

​ 向响应式对象中添加一个 property,并确保这个新 property 同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新 property,因为

//Vue 无法探测普通的新增 property (比如
this.myObject.newProperty = 'hi'
//动态更新对象,引起视图变化
this.$set(this.obj,"age",20);

API参考地址:https://cn.vuejs.org/v2/api/#vm-set

8. 计算属性与侦听器

  1. 计算属性

    计算属性缓存 vs 方法:

    ​ 我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。

  2. 侦听器

    Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。

9. Class 与 Style 绑定

9.1 绑定 HTML Class

对象语法
数组语法:注意事项:必须是数组嵌套对象

9.2 绑定 HTML Style

对象语法
数组语法
注意:自动添加前缀,当 v-bind:style 使用需要添加浏览器引擎前缀的 CSS property 时,如 transform,Vue.js 会自动侦测并添加相应的前缀。

10. 组件

10.1组件的创建:单文件组件

Vue组件由三个部分组成:HTML结构JS逻辑CSS样式。必须存在的是视图结构部分

10.2 组件引入

//1.导入
import MyComponent from './components/MyComponent.vue'
export default {  
 name: 'App',  
 components: {    
     HelloWorld,
     //2.注入
     MyComponent  
 }
}
//3.template中加载
<my-component/><MyComponent/>

10.3 组件的复用:

组件的每一次引用都是独立的实例对象
应用场景:列表数据的操作中,例如购买数量的选择。把每一个列表单独抽离成一个组件

10.4 data必须是一个函数

​ 函数才能作为构造方法,创建新的独立的实例对象。引入多个同一组件的同一数据才会有自己独立的数据属性。

​ 组件被定义,data 必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例。如果 data 仍然是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!通过提供 data 函数,每次创建一个新实例后,我们能够调用 data 函数,从而返回初始数据的一个全新副本数据对象。(也就是说写成函数,会有函数作用域的概念 ,是私有函数,只作用到当前组件中)

10.5 组件的组织

三、知识点2

1.删除Vue-cli预设

​ 在用户根目录下(C:\Users\你的用户名)这个地址里有一个.vuerc 文件,修改或删除配置。

2.组件

示例源码在02VueBase/Vue-demo

2.1 Props(组件之间的数据传递)

  1. Prop 的大小写 (camelCase vs kebab-case)不敏感
  2. Prop 类型: String Number Boolean Array Object Date Function Symbol
  3. 传递静态或动态 Prop
  4. 单向数据流:只能父传子,不能子传父
  5. Prop 验证:
    类型验证---------空验证(是否允许为空) ---------默认值(缺省值)
    注意:对象或数组默认值必须从一个工厂函数获取

2.2 自定义事件

子传父----数据传输

this.$emit("oneData",this.msg);

.sync修饰符

<child @update:msg2Event="msg2Handle"/>  
<child :msg2Event.sync="msg2"/>   //同上

sendMsg2Handle(){
    this.$emit("update:msg2Event",this.data);
}

2.3 插槽

  1. 插槽内容:tab切换
  2. 编译作用域:父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。(动态数据写在哪里就在哪里声明)
  3. 后备内容(默认值,缺省值)
  4. 具名插槽
  5. 作用域插槽
  6. 解构插槽 Prop
  7. 具名插槽的缩写 v-slot: ----> #
  8. 废弃了的语法(了解性知识)

2.4 动态组件 & 异步组件

  1. 动态组件:keep-alive
    include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
    exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
    max - 数字。最多可以缓存多少组件实例。
  2. 异步组件:程序运行时不加载组件,什么时候组件被使用了,才会被加载

2.5 处理边界情况

$root property

$parent

2.6 Vue 实例

Vue是MVVM的模型,但是大家记住,他并不是完整的MVVM
M:Model
VM:ViewModel
V:View
MVC标准的设计模型,Angular

2.7进入/离开 & 列表过渡

  • 在 CSS 过渡和动画中自动应用 class

  • 可以配合使用第三方 CSS 动画库,如 Animate.css

2.8自定义指令

  1. 全局指令
//1.在main.js中设置
//全局指令---设置输入框焦点
Vue.directive('focus',{
    inserted(ele){
        ele.focus();
        //focus是js获取input焦点的方法
    }})
//2.在src文件夹中新建指令文件夹,存放指令.js
//在js文件中写自定义指令,然后在main.js 中引入
import Vue from 'vue'Vue.directive('focus', {//全局指令
    inserted(ele) {
        ele.focus();//focus是js获取input焦点的方法
    }})
  1. 局部指令
    export default {  
        name:'',
        data () {
            return {};
        },  
        // 局部指令:在当前组件中使用
        directives:{
            focus1:{
                inserted(ele){//ele为当前dom节点
                    ele.focus();
                }
            },
            red:{
                inserted(ele){
                    ele.style.color='#ff0000'
                }
            }
        }
        }
    

    自定义指令存在五个钩子函数
    bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
    inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
    update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
    componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
    unbind:只调用一次,指令与元素解绑时调用。

    钩子函数的参数 (即 elbindingvnodeoldVnode)。

2.9渲染函数 & JSX

2.10 过滤器:

商城平台,价格¥

1.局部过滤器
//<p>{{ msg | author }}</p>
export default {
  name: "",
  data() {
      return {
          msg: "高山仰止,景行行止。",
      };
  },
  filters: {
      author(value) {
          if (value) {
              return value + "-----佚名";
          }
      },
  }};
2.全局过滤器
//1.main.js中
Vue.filter('rmb',(value)=>{
  //value就是{{}}或v-bind绑定的值
  if(value){
      return '¥'+value;
  }})
//建议采用第二种↓
//2.在src文件夹下--新建filter文件夹,在其中创建js文件,存放局部过滤器
import Vue from 'vue'Vue.filter('rmb',(value)=>{
  //value就是{{}}或v-bind绑定的值
  if(value){
      return '¥'+value;
  }
})

四、axios

文档参考地址:https://github.com/axios/axios

中文文档参考地址:https://www.kancloud.cn/yunye/axios/234845

1.安装

npm install --save axios

2.使用

2.1 在main.js中引入axios

// 引入axios
import axios from 'axios'
// 挂载到全局
Vue.prototype.$axios=axios

2.2 使用

mounted() {
//1get请求
this.$axios
  .get("http://iwenwiki.com/api/FingerUnion/recommend.php", {
    params: {
      page: 1,
    },
  })
  .then((res) => {
    console.log(res);
  });
},
/**
 * 2.post请求
 * axios接受参数数据的类型: user_id=iwen@qq.com&password=iwen123&verification_code=crfvw
 */
this.$axios
  .post(
    "http://iwenwiki.com/api/blueberrypai/login.php",
    qs.stringify({
      user_id: "iwen@qq.com",
      password: "iwen123",
      verification_code: "crfvw",
    })
  )
  .then((res) => {
     console.log(res.data);
  });
/**
 * 3.执行多个并发请求
 */
const getBanner=()=>{
  return  this.$axios.get(
    "http://iwenwiki.com/api/blueberrypai/getIndexBanner.php"
  );
}
const getChating=()=> {
  return  this.$axios.get(
    "http://iwenwiki.com/api/blueberrypai/getIndexChating.php"
  );
}
this.$axios
  .all([getBanner(), getChating()])
  .then(this.$axios.spread((banner, chating) => {
    // 这里的执行,一定是banner和chating同时都拿到数据之后执行
    console.log(banner.data,chating.data);
  }));

3.封装网络请求

//request.js
/**
* 封装
* 网络请求:axios */
import axios from "axios";
import qs from 'querystring'
//处理异常错误
const errorHandle = (status, info) => {
    switch (status) {
        case 400:
            console.log('服务器收到客户端通过PUT或者POST请求提交的表示,表示的格式正确,但服务器不懂它什么意思');
            break;
        case 401:
            console.log('客户端试图对一个受保护的资源进行操作,却又没有提供正确的认证证书');            break;
        case 403:
            console.log('客户端请求的结构正确,但是服务器不想处理它');
            break;
        case 404:
            console.log('资源未定义');
            break;
        case 500:
            console.log('执行请求处理代码时遇到了异常,它们就发送此响应代码');
            break;
        case 503:
            console.log('服务器端资源不足,无法处理该请求');
            break;
        default:
            console.log(info);
            break;
    }}
// 创建axios实例对象
const instance = axios.create({
    timeout: 5000
})
// 处理并发请求方法
instance.all = axios.all;
instance.spread = axios.spread;
//全局配置
// instance.defaults.baseURL = 'http://iwenwiki.com'
instance.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
// 请求拦截和响应拦截
instance.interceptors.request.use(
    config => {
        if (config.method == 'post') {
            // post请求转换数据格式
            config.data = qs.stringify(config.data);
        }
        return config;
    },
    error => Promise.reject(error)
)
instance.interceptors.response.use(
    response => response.status == 200 ? Promise.resolve(response) :
    Promise.reject(response),
    error => {
        const { response } = error;
        if (response) {
            /**
            * 错误信息以状态码为主
            */
            errorHandle(response.status, response.data);
            return Promise.reject(response);
        } else {
            console.log('请求被中断');
        }
    })
/**
* 提供get和post的请求方式
*/
export function get(url, params) {
    return new Promise((resolve, reject) => {
        instance.get(url, {
            params
        }).then(res => {
            resolve(res.data);
        }).catch(err => {
            reject(err.data);
        })
    })}
export function post(url, params) {
    return new Promise((resolve, reject)=>{
        instance.post(url,params).then(res=>{
            resolve(res);
        }).catch(err=>{
            reject(err);
        })
    })}
export default instance
//index.js
import { get, post } from "../utils/request";
// console.log(get,post);
const api = {
    /**
    * 轮播信息列表接口
    * @param {Object} params get参数
    * @returns 返回请求后的数据
    */
    getBanner(params) {
        return get('http://iwenwiki.com/api/blueberrypai/getIndexBanner.php', params)
    },
    /**
    * 登录验证接口
    * @param {Object} params post参数
    * @returns  返回请求后的数据
    */
    Login(params) {
        return post('http://iwenwiki.com/api/blueberrypai/login.php', params)
    }}
export default api;
//main.js
import api from './api/index'Vue.prototype.$api=api
//组件中使用
mounted() {
    //请求banner信息
    this.$api.getBanner().then((res) => {
        console.log(res.banner);
        this.banner = res.banner;
    });
    //登录
    this.$api
        .Login({
        user_id: "iwen@qq.com",
        password: "iwen123",
        verification_code: "crfvw",
    })
        .then((res) => {
        console.log(res.data);
    });
}

4.跨域处理

//创建vue.config.js
module.exports = {
    devServer: {
        proxy: {
            '/fct': {
                target: 'http://iwenwiki.com:3002',
                changeOrigin: true,
                pathRewrite: {
                    "^/fct": ''
                }
            }
        }
    }
}

//接口js文件
import {get} from '../utils/request'

const api={
    getList(params){
        return get('/fct/api/banner',params)
    }
}
export default api

4.1 关于在工程化中处理跨域的问题

  1. 开发环境(写代码的环境)
    解决跨域的方案:proxy代理 cors服务器端
  2. 生成环境(打包上线到服务器的环境)
    解决跨域的方案:cors

4.2cors和proxy解决方案

  1. cors服务器端的解决方案:
    如果使用了此方案,我们前台不需要做任何的处理,就像没有跨域一样

  2. proxy前台解决方案:
    通过配置proxy实现代理跨域处理
    重点注意:此种方案只适合开发场景下,如果是上线,则不可以使用。
    那么如果使用的proxy代理,打包上线怎么办:仍然需要后台配置cors的形式

4.3 开发形式:

  1. 服务器存在,单纯重构或重新开发前端
  2. 服务器端存在,前端进行更新操作
  3. 前后端同步开发,都是从零开始的
    问题:我们需要自己模拟数据:mock、json数据、node.js增加本地服务器处理(proxy)

五、Vue Router路由

1.基本使用

1.1 安装

npm i -s vue-router

1.2 引入路由

//新建路由配置文件夹,创建index.js
import Vue from 'vue'import VueRouter from 'vue-router'
Vue.use(VueRouter)

1.3 路由配置流程

  1. 定义路由组件(创建路由要显示的页面)
  2. 定义路由
//index.js
const routes = [
    {
        path: '/home',
        name: 'Home',
        component: Home
    },
    {
        path: '/shop',
        name: 'Shop',
        component: Shop
    }]
//3.创建router 实例
const router=new VueRouter({  routes})
//导出routerexport default router
//在main.js中引入index.js
import router from './router/index'
new Vue({
    render: h => h(App),
    router//4,挂载到Vue实例
}).$mount('#app')

1.4 配置路由出口

//路由匹配到的组件将渲染到这里//放在App.vue视图中呈现<router-view></router-view>

1.5 配置路由导航

<ul>
    <li><router-link to="/home">首页</router-link></li> |   
    <li><router-link to="/shop">商城</router-link></li>
</ul>

2.动态路由匹配

  1. 设置路由路径上的key: path:'/user/:user'

  2. 设置跳转携带的参数: <router-link to="/user/付常涛">去用户中心</router-link>

    动态属性:<router-link :to="'/user/'+name">去用户中心</router-link>

  3. 进入跳转后的页面读取参数:{{$route.params.user}}

3.嵌套路由

  1. 创建视图文件 xxx.vue

  2. 配置路由

    {  
        path: '/shop',
        name: 'Shop',
        component: Shop,
        //嵌套路由,设置子路由
         children:[
             {
                 path:'/shop/book',
                 component: ()=>import ('../view/child/Book')
             },{
                 path:'news',
                 component:()=>import('../view/child/News')
             }
         ],
          redirect:'/shop/book'
    }
    
  3. 在需要设置子路由的视图文件添加:

    <router-view></router-view>router-link

4.编程式导航

4.1 push

this.$router.push('/user/fct')//或this.$router.push({    path:'/user/fct'})

4.2 replace

this.$router.replace('/user/fct')

5.命名路由

  1. 为每个路由添加名称name
  2. 应用:
//1.使用命名路由进行编程式跳转
this.$router.push({
    name:'User',
    params:{user:'常青i'}
})
//2.router-link跳转
<router-link :to="{name:'User',params:{user:'咕咕咕'}}">去用户中心</router-link>

6.重定向和别名

const routes = [
    {
    path: '/',
    //1.重定向
    redirect: '/home'
},{
    path: '/home',
    name: 'Home',
    //2.别名--不同路径显示同一个页面
    alias:'/first',
    component: Home
}
]

7.路由组件传参

  1. 设置路由
{
    path: '/details/:id',
    name: 'Details',
    component: Details,
    //设置使用props
    props:true
}
  1. 获取/使用参数
/*获取参数*/
<p>产品ID:{{$route.params.id}}</p>
<p>产品ID:{{id}}</p>
export default {
    props:{
        id:{
            type:[String,Number],
            default:1
    }
}}

8.HTML5 History 模式

8.1 history :

/ (pushState)

8.2 hash:

# (锚点)

当你使用 history 模式时,URL 就像正常的 url,例如 http://yoursite.com/user/id,也好看!

不过这种模式要玩好,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404,这就不好看了。

//设置模式
const router = new VueRouter({
    routes,
    //默认hash
    mode: 'history'
})

六、路由进阶

1.导航守卫

  • 全局前置守卫 router.beforeEach((to, from, next) => {...})

  • 全局解析守卫 router.beforeResolve((to, from, next) => {...})

  • 全局后置钩子 router.afterEach((to, from) => {...})

  • 路由独享的守卫 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`  
    }
    

1.2 完整的导航解析流程:

  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter
  8. 调用全局的 beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

2.路由元信息

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

const routes = [
    {
        path: '/about',
        name: 'About',
        component: () => import('../views/About.vue'),
        meta:{
        isLogin:true
    }}]
//导航前置守卫
router.beforeEach((to, from, next) => {
    //判断页面是否需要登录
    if(to.meta.isLogin){
        //用户是否登录
        const token=false;
        if(token){
            next();
        }else{
            next('/login');
        }
    }
    next();
})

3.数据获取

  • 导航完成之后获取:先完成导航,然后在接下来的组件生命周期钩子中获取数据。在数据获取期间显示“加载中”之类的指示。
export default {
    created() {
        // axios.get(''),在此进行数据请求
    }}
  • 导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后执行导航。
export default {
    beforeRouteEnter(to, from, next) {
        axios
            .get('http://iwenwiki.com/api/blueberrypai/getIndexBanner.php')
            .then(res => {
            // console.log(res.data);
            next(vm => {
                vm.banner=res.data.banner;
                console.log(vm.banner);
            });
        })
    }}

4.路由高亮

  • router-link-active(包含效果)

  • router-link-exact-active(精准匹配路由)

#nav a.router-link-exact-active {  color: #42b983;}

也可以在路由配置文件中设置简写

const router = new VueRouter({  routes,  //精准匹配  linkExactActiveClass:'active'})
//app.vue中设置全局样式.active{  color: #42b983 !important;}

七、数据传递

1.组件间的数据传递

  • 父组件-----》子组件------props
  • 子组件-----》父组件------自定义事件
//Parent.vue
<template>
  <div>
    <!-- 2.msg即为Child向Parent传递的数据 -->
    Parent:{{ msg }}
    <!-- 1.title即为Parent向Child传递的数据 -->
    <Child title="标题1" @onMyEvent="getMsg" />
  </div>
</template>
<script>
    import Child from "./Child.vue";
    export default {
        name: "Parent",
        components: {
            Child,
        },
        data() {
            return {
                msg: "",
            };
        },
        methods: {
            //2.msg即为Child向Parent传递的数据
            getMsg(data) {
                console.log(data);
                this.msg = data;
            },
        }};
</script>
//Child.vue
<template>
  <div>
    <!-- 1.title即为Parent传递的数据 -->
    Child:{{ title }}
    <!-- 2.向Parent传递数据 -->
    <button @click="sendMsg">子传给父数据</button>
  </div>
</template>
<script>
    export default {
        props: {
            //1.获得Parent传递的数据
            title: {
                type: String,
                default: ""
            }
        },
        methods: {
            sendMsg() {
                //2.向Parent传递数据
                this.$emit("onMyEvent","Child传给Parent的数据");
            }
        }};
</script>

2.event-bus

数据传递解决方案:

​ 通过EventBus传递数据,组件之间的关系不是必须有关联

eventbus可以理解全局的数据传递

//1.创建工具包-->新建event-bus.js,设置如下
import Vue from 'vue'
// ①不挂载:
export const EventBus = new Vue();
const EventBus = new Vue();
Object.defineProperties(Vue.prototype, {//②挂载到全局Vue对象上
    $bus: {
        get: function () {
            return EventBus;
        }
    }})
//2.在main.js
import './utils/event-bus'
<!-- AComponent.vue 发送数据-->
<template>
  <div>
    AComponent
    <button @click="sendHandle">EventBus发送数据</button>
  </div>
</template>
<script>
    // ①:import {EventBus} from '../utils/event-bus'
    export default {
        methods: {
            sendHandle() {
                // ①:EventBus.$emit("msg","event-bus数据");
                this.$bus.$emit("msg", "event-bus数据");
            },
        },
    };
</script>
<!-- BComponent.vue 接受-->
<template>
<div>BComponent:{{ msg }}</div>
</template><script>
    // ①:import { EventBus } from "../utils/event-bus";
    export default {
        name: "BComponent",
        data() {
            return {
                msg: "",
            };
        },
        mounted() {
            // ①:EventBus.$on("msg", (res) => {
            //   this.msg = res;
            // });
            this.$bus.$on("msg", (res) => {
                this.msg = res;
            });
        },
        methods: {},};
</script>

八、Vue插件

1.Echarts

Vue 中引入Echarts

  1. 安装

    npm i -s echarts

    最新版本已无map文件,需要则下载4版本npm i -s echarts@4

  2. 引入

    import * as echarts from 'echarts'

  3. 使用地图

    在main.js中引入

    import '../node_modules/echarts/map/js/china'

//echarts.js
const install = function (Vue) {
    // 绑定到Vue原型上
    Object.defineProperties(Vue.prototype, {
        $charts: {
            get() {
                return {
                    // 编写Echarts视图
                    line: function (id) {
                        this.chart = echarts.init(document.getElementById(id));
                        // option就是参数
                        let option = {
                            xAxis: {
                                type: 'category',
                                data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat',
                                       'Sun']
                            },
                            yAxis: {
                                type: 'value'
                            },
                            series: [{
                                data: [150, 230, 224, 218, 135, 147, 260],
                                type: 'line'
                            }]
                        };
                        this.chart.setOption(option);
                    }
                }
            }
        }
    })}

2.Swiper

参考网址:https://github.com/surmon-china/vue-awesome-swiper

下载:

npm install swiper vue-awesome-swiper --save

2.1 引入

import VueAwesomeSwiper from 'vue-awesome-swiper'
// import style (>= Swiper 6.x)
//import 'swiper/swiper-bundle.css'
// import style (<= Swiper 5.x)
import 'swiper/css/swiper.css'Vue.use(VueAwesomeSwiper)

九、项目

1.疫情动态展示

地址:

2.音乐播放器

地址:

十、Vuex

1. 简介

​ Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

2. state

​ Vuex 使用单一状态树——是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源 (SSOT)”而存在。这也意味着,每个应用将仅仅包含一个 store 实例。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。

  1. 读取state状态
    this.$store.state.count
  2. mapState 辅助函数
<p>{{ count }}</p>
import { mapState } from 'vuex'
export default {
    name: 'HelloWorld',
    computed: {
        //...mapState({
        //  count: state =>state.count,
        //   myCount: 'count'    
        //})
        ...mapState(["count"])
    }
}

3. mutations

更改 Vuex 的 store 中的状态唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:
每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。
这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数

  1. 基础调用
    this.$store.commit("increment")

  2. 提交载荷(提交参数)

    this.$store.commit("increment",参数)

  3. 对象风格的提交方式

  4. Mutation 需遵守 Vue 的响应规则
    a:最好提前在你的 store 中初始化好所有所需属性。
    b:当需要在对象上添加新属性时,你应该使用 Vue.set(obj, 'newProp', 123), 或者以新对象替换老对象
    注意:在Vue中,对象属性的增加或减少,必须通过Vue.set去改变!!!

  5. 使用常量替代 Mutation 事件类型

  6. Mutation 必须是同步函数

  7. 在组件中提交 Mutation(快捷方案)

const store = new Vuex.Store({
    state: {
        count: 0
    },
    mutations: {
        increment(state, n) {
            state.count += n;
        },
        decrement(state, n) {
            state.count -= n;
        }
    })}    

4. actions

Action 类似于 mutation,不同在于:
Action 提交的是 mutation,而不是直接变更状态。

this.$store.dispatch("asyncGetBanner");

​ Action 可以包含任意异步操作。

  1. 在组件中分发 Action
    注意事项:state是在computed中使用,而mutationsactions是在methods中使用
const store = new Vuex.Store({
    state: {
        count: 0
    },
    mutations: {
        increment (state) {
            state.count++
        }
    },
    actions: {
        increment (context) {
            context.commit('increment')
        }
    }})

5. getters

有时候我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤计数

  1. mapGetters 辅助函数
const store = new Vuex.Store({
    state: {
        count: 10
    },
    getters: {
        getCount(state){
            if(state.count > 0){
                return state.count;
            }else{
                return '数据无意义'
            }
        }
    }})
<p>{{ this.$store.getters.getCount }}</p>
<p>{{ getCount }}</p>
<!-- mapGetters 辅助函数 -->
<script>
    import { mapGetters } from 'vuex'
    export default {
        name: 'HelloWorld',
        computed: {
            ...mapGetters(["getCount"])
        },
</script>

6. module

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

  1. 命名空间
  2. 模块的局部状态
  3. 快捷读取方案
  4. 在带命名空间的模块内访问全局内容
//index.js
const store = new Vuex.Store({
    modules: {
      cartModule: {
        //   命名空间
          namespaced: true,
          state: {
            goods: {
                name: '手表',
                logo: '百达翡丽',
                price: '200000',
                desc: '百达翡丽,不仅仅如此。'
            }
          },
          mutations: {
            setGoods(state,goods) {
                state.goods = goods;
            }
          },
          actions: {
            asyncSetGoods({ dispatch, commit, getters ,rootGetters }, goods){
                commit("setGoods",goods)
            }
          },
          getters: {
            getGoods(state,getters,rootState,rootGetters) {
                return state.goods
            }
          }
      },
      MusicModule: {
        namespaced: true,
        state: {
          song: {
              name: '完',
              author: '陈奕迅',
              place: '网易',
              desc: 'Eason Chan'
          }
        }
    }
    }
})
<template>
<div>
    <div>
        <h3>购物车</h3>
        <p>{{ this.$store.state.cartModule.goods }}</p>
        <p>{{ this.$store.getters["cartModule/getGoods"] }}</p>
        <!--快捷方案-->
        <p>{{ goods }}</p>
        <p>{{ getGoods }}</p>
        <button @click="changeHandle">更换手表</button>
    </div>
    <div> 
        <h3>音乐播放</h3>
        <p>{{ this.$store.state.MusicModule.song }}</p>
        <p>{{ song }}</p>
    </div>
    </div>
</template>
<script>
    import { mapState, mapMutations, mapActions, mapGetters } from "vuex";
    export default {
        name: "demo",
        data() {
            return {
                info: {
                    name: "手环",
                    logo: "小米",
                    price: "229",
                    desc: "小米手环,自带精彩。",
                },
            };
        },
        computed: {
            ...mapState("cartModule", {
                goods: (state) => state.goods,
            }),
            ...mapState("MusicModule", {
                song: (state) => state.song,
            }),
            ...mapGetters("cartModule", {
                getGoods: "getGoods",
            }),
        },
        methods: {
            ...mapMutations("cartModule", ["setGoods"]),
            ...mapActions("cartModule", ["asyncSetGoods"]),
            changeHandle() {
                //基本方案
                // this.$store.commit("cartModule/setGoods", this.info);
                // this.$store.dispatch("cartModule/asyncSetGoods", this.info);
                //快捷方案
                // this.setGoods(this.info)
                this.asyncSetGoods(this.info);
            },
        },
    };
</script>

7.项目结构

//index.js
import Vue from 'vue'
import Vuex from 'vuex'
import state from  "./state"
import mutations from "./mutations"
import actions from "./actions"
import getters from "./getters"
import cartModule from "./modules/cartModule"
import MusicModule from "./modules/MusicModule"

Vue.use(Vuex)

const store = new Vuex.Store({
    state,
    mutations,
    actions,
    getters,
    modules: {
      cartModule,
      MusicModule
    }
})

export default store

8.表单处理

动态数据绑定,vuex

<template>
  <div class="about">
    <!--方式1-->
    <div>
      <input type="text" :value="msg" @input="changeHandle">
      <p>{{ msg }}</p>
    </div>
    <!--方式2--> 
    <div>
      <input type="text" v-model="message">
      <p>{{ message }}</p>
    </div>
  </div>
</template>
<script>
import { mapState } from "vuex"
export default {
  name: 'About',
  computed: {
    ...mapState(["msg"]),
    //方式2:双向绑定的计算属性
    message: {
      get() {
        return this.$store.state.message
      },
      set(value){
        this.$store.commit("setMessage",value)
      }
    }
  },
  methods: {
    changeHandle(e){
      this.$store.commit("setMsg",e.target.value)
    }
  }
}
</script>

9.热重载和热更新

webpack热更新:浏览器自动刷新

热重载:只更新发生变化的位置

十一、第三方UI

1.Element UI

Element,一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库

地址:https://element.eleme.cn/#/zh-CN

新版本的VueCli安装方案:

vue add element
  1. Fully import 全引入
  2. Import on demand 按需引入

开发环境:全引入

生产环境:删除不用的部分

2.Vant

轻量、可靠的移动端 Vue 组件库

2.1 安装

# Vue 2 项目,安装 Vant 2:
npm i vant -S

# Vue 3 项目,安装 Vant 3:
npm i vant@next -S

2.2 引入组件

#自动按需引入组件 (推荐)
# 安装插件
npm i babel-plugin-import -D
//在 babel.config.js 中配置
module.exports = {
  plugins: [
    ['import', {
      libraryName: 'vant',
      libraryDirectory: 'es',
      style: true
    }, 'vant']
  ]
};

2.3 使用

//创建插件对应的vant.js
// 接着你可以在代码中直接引入 Vant 组件
// 插件会自动将代码转化为方式二中的按需引入形式
import Vue from 'vue';
import { Button, Calendar, Cell } from 'vant';

Vue.use(Cell)
Vue.use(Button)
Vue.use(Calendar)
posted @ 2021-08-13 10:58  青柠i  阅读(226)  评论(0编辑  收藏  举报