Vue知识点总结

Vue

一、概述

1.1 vue介绍

vue是一种构建用户界面的渐进式框架。

1.2 vue特点

响应式

当在浏览器中修改对应变量的值,能够即时渲染到页面上生效。

1.3 npm相关命令

npm init -y  # 管理工作目录
npm list [-g] --depth 0  # 查看安装好的npm包
npm install xxx@1.0 [-g]  # 安装某个版本的包,本地安装则不添加-g
npm uninstall xxx [-g]  # 卸载npm包,卸载有问题可以直接去对应的模块目录中删除

1.4 vue-cli相关命令

# 2版本的vue-cli
npm install vue-cli  # 安装vue-cli脚手架工具
vue init webpack xxx  # 初始化vue目录
cd xxx  # 进入vue工作目录
npm run dev  # 启动dev环境
npm run build

1.5 ES6常用语法

  • 变量提升(let关键字、块级作用域、const常量)
  • 模板字符串(${}
  • 数据解构
  • 类的定义
  • 箭头函数(箭头函数的this是上下文,普通函数的this是最近的调用者)

二、Vue操作

2.1 vue实例

let vm = new Vue({
  el: "选择器",
  data: {}  // 数据对象
})

备注:vue的响应式要求,响应的数据属性必须在实例创建之前已经定义在vue中,新增的vue数据属性不会触发响应式动作。

2.2 常用指令

2.2.1 v-text和v-html

用来展示Html代码

<div id="app">
  	<!-- 这个和v-text="msg"作用相同 -->
    <div>{{hello}}</div>
		<!-- 展示html代码需要用v-htmnl来表示 -->
    <div v-html="mySpan"></div>
</div>
<script>
    let vm = new Vue({
        el: "#app",
        data: {
            hello: "helloworld",
            mySpan: "<span style='color: red;'>red helloworld</span>"
        }
    })
</script>
2.2.2 v-bind

用来给元素绑定属性

<div id="app">
    <div v-bind:title="title">{{hello}}</div>
</div>
<script>
    let vm = new Vue({
        el: "#app",
        data: {
            hello: "悬停鼠标",
          	// 实际上就是 <div title="xxx"></div>
            title: "页面加载于" + new Date().toLocaleString()
        }
    })
</script>
2.2.3 v-show和v-on

v-show用来控制css的display属性,变化时不需要重新渲染页面;

v-on用来给标签绑定事件;

v-on相关的装饰器
.stop - 调用 event.stopPropagation()。
.prevent - 调用 event.preventDefault()。
.capture - 添加事件侦听器时使用 capture 模式。
.self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
.{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
.native - 监听组件根元素的原生事件。
.once - 只触发一次回调。
.left - (2.2.0) 只当点击鼠标左键时触发。
.right - (2.2.0) 只当点击鼠标右键时触发。
.middle - (2.2.0) 只当点击鼠标中键时触发。
.passive - (2.3.0) 以 { passive: true } 模式添加侦听器
<div id="app">
    <h2 v-show="flag">helloworld</h2>
    <button v-on:click="myClick">button</button>
</div>
<script>
    let vm = new Vue({
        el: "#app",
        data: {
            flag: true
        },
        methods: {
            myClick: function (event) {
                this.flag = ! this.flag
            }
        }
    })
</script>
2.2.4 v-if和v-else

通过v-if来操作的dom元素会重新渲染,注意与show区分

<div id="app">
    <h2 v-if="flag">yes</h2>
    <h2 v-else="flag">no</h2>
    <button v-on:click="myClick">button</button>
</div>
<script>
    let vm = new Vue({
        el: "#app",
        data: {
            flag: true
        },
        methods: {
            myClick: function (event) {
                this.flag = ! this.flag
            }
        }
    })
</script>
2.2.5 v-for

用于循环处理数据

<div id="app">
    <h2 v-for="item in items">
        {{item.name}}
    </h2>
</div>
<script>
    let vm = new Vue({
        el: "#app",
        data: {
            items: [
                {name: "shenzhen", size: "small"},
                {name: "beijing", size: "big"}
            ]
        },
    })
</script>
2.2.6 v-model

用于给表单控件做数据双向绑定

<div id="app">
    <input type="text" v-model="value">
    <h4>文本框: {{value}}</h4>
    <input type="checkbox" value="shenzhen" v-model="city" id="shenzhen">
    <label for="shenzhen">深圳</label>
    <input type="checkbox" value="beijing" v-model="city" id="beijing">
    <label for="beijing">北京</label>
    <input type="checkbox" value="shanghai" v-model="city" id="shanghai">
    <label for="shanghai">上海</label>
    <h4>复选框: {{city}}</h4>
</div>
<script>
    let vm = new Vue({
        el: "#app",
        data: {
            value: "",
            city: []
        }
    })
</script>

2.3 计算属性

计算属性返回的是函数。对于复杂的计算逻辑,应该使用计算属性。计算属性的数据是放入缓存的,当数据改变时会重新计算。

<div id="example">
  <p>Original message: "{{ message }}"</p>
  <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>

<script>
var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {
    // 计算属性的 getter
    reversedMessage: function () {
      // `this` 指向 vm 实例
      return this.message.split('').reverse().join('')
    }
  }
})
</script>

2.4 数据监听watch

数据监听watch主要是用来监听数据改变的,可以观测到新旧值的改变,从而做出一些处理。但是这里有个Bug,数据监听的对象如果是值类型数据,那么可以正确判断出数据发生改变。而对于像列表对象,如果索引值发生变化,是没法感知到的。

// 数据监听的一个Bug,本质是由于数组是个对象,值改变但是地址没有变化,没有监听到
new Vue({
    el: "#app",
    data: {
        name: "zimsky",
        hobby: ["1", "2"]
    },
    methods: {
        myClick: function(){
            // this.name = "zimskyzeng"
            // this.hobby[0] = "2" 这里的改变索引值不会生效
            app.$set(this.hobby, 0, "2")  // 需要用手动刷新的方式能够生效,显示刷新数据
        }
    },
    watch: {
        name: {
            handler: function(val, oldVal) {
                console.log(val)
                console.log(oldVal)
            },
            deep: true  //深度监听也没法生效
        }
    }
})

2.5 根实例属性获取

在vue中的根实例属性,都可以通过this.$方式来获取,例如this.$elthis.$data等。

三、组件

3.1 全局组件注册

<!-- 写法1 使用这种写法会将app标签替换成<div><span>{{msg}}</span></div>-->
<!-- 组件可以接受的选项除了没有el外,和Vue实例基本一致,有data computed watch methods等-->
<div id="app"></div>
<script>
    Vue.component("global-component", {  // 没有el标签
        template: `<div><span>{{msg}}</span></div>`,
        data() {  // 这里的data必须是函数
            return {
                msg: "helloworld",
            }
        }
    });
    let vm = new Vue({
        el: "#app",
        template: `<global-component></global-component>`
    });
<!-- 写法2 使用这种写法会保留id="app"标签,在内部添加component内容 -->
<div id="app">
    <global-component></global-component>
</div>
<script>
    Vue.component("global-component", {
        template: `<div><span>{{msg}}</span></div>`,
        data() {
            return {
                msg: "helloworld",
            }
        }
    });
    let vm = new Vue({
        el: "#app",
    });

3.2局部组件注册

<div id="app"></div>
<script>
    let localComponent = {  // 写法上是注册全局组件的对象
        template: `<div>{{ greeting }}</div>`,
        data(){
            return {
                greeting: "helloworld"
            }
        }
    };
    let anotherComponent = {
        template: `<div>{{ a }}</div>`,
        data(){
            return {
                a: "aaa"
            }
        }
    };
    let vm = new Vue({
        el: "#app",
        template: `<div><my-component></my-component><another-component></another-component></div>`,  // 这里写了template意味着app的div被替换掉
        components: {
            "my-component": localComponent,
            "another-component": anotherComponent,
        }
    })
</script>

局部组件嵌套时的关系,如下代码

<div id="app"></div>
<script>
    let localComponent = {
        template: `<div>{{ greeting }}</div>`,
        data(){
            return {
                greeting: "helloworld"
            }
        }
    };
    let App = {
        template: `<localComponent></localComponent>`,  // 这里的localComponent在下面的对象中注册
        components: {
            "localComponent": localComponent
        }
    };

    let vm = new Vue({  // vue实例是执行入口,资源开始会从这里找
        el: "#app",
        template: `<App></App>`,  // 这里的App在下面的对象中注册,键值相同仅需写值
        components: {
            App,
        }
    })

组件执行过程以及显式标签写法如下

<div id="app">
    <App></App>  <!-- vue执行到这里时,会去vue实例中寻找组件 -->
</div>
<script>
    let localComponent = {
        template: `<div>{{ greeting }}</div>`,
        data(){
            return {
                greeting: "helloworld"
            }
        }
    };
    let App = {
        template: `<localComponent></localComponent>`,
        components: {
            "localComponent": localComponent
        }
    };

    let vm = new Vue({
        el: "#app",
        // template: `<App></App>`, 注释掉这行,则需要显式在html中写组件标签
        components: {
            App,  // 标签名为App
        }
    })
</script>

3.3 父子组件之间传递消息

<div id="app">
    <App></App>
</div>
<script>
    let localComponent = {  // 子组件
        template: `<div>{{ greeting }} {{myAttr}}</div>`,
        data(){
            return {
                greeting: "helloworld"
            }
        },
        props: ["myAttr",]  // 从这里获取父组件的属性
    };
    let App = {  // 父组件
        template: `<localComponent v-bind:myAttr="attr"></localComponent>`,  // 传递方式为绑定自定义属性,attr对应data中的变量名
        components: {
            "localComponent": localComponent
        },
        data(){
            return {
                attr: "zimsky"  // attr对应变量名
            }
        }
    };

    let vm = new Vue({
        el: "#app",
        // template: `<App></App>`,
        components: {
            App,
        }
    })
</script>

3.4 子父通信

<script>
  let localComponent = {
    template: `
        <div>
            <button @click="myTicket">按钮</button>
        </div>`,
    data() {
      return {
        greeting: "helloworld",
      }
    },
    methods: {
      myTicket: function (event) {
        // 使用 this.$emit来向父组件提交事件
        this.$emit("doTicket", "myTicket");
      }
    }
  };
  let App = {
    // 父组件监听子组件事件
    template: `
        <div><h1>{{title}}</h1>
            <localComponent @doTicket="doTicket"></localComponent>
        </div>`,
    components: {
      localComponent
    },
    data() {
      return {
        title: "title",
      }
    },
    methods: {
      doTicket: function (val) {
        this.title = val
      }
    }
  }
  var vm = new Vue({
    el: "#app",
    components: {
      App,
    }
  })
</script>

3.5 非父子通信

let mid = new Vue() // 中间调度器

let component1 = {
    template: `<div>{{message}}<button @click="clickMe">change</button></div>`,
    data(){
        return {
            message: "com1"
        }
    },
    methods: {
        clickMe: function () {
            mid.$emit("changeData", this.message)
        }
    }
}

let component2 = {
    template: `<div>{{message}}</div>`,
    data(){
        return {
            message: "com2"
        }
    },
    mounted(){
        mid.$on("changeData", data => {this.message = data})
    }
}

let vm = new Vue({
    el: "#app",
    data: {
        name: "shenzhen"
    },
    template: `<div><component1></component1><component2></component2></div>`,
    components: {
        component1,
        component2
    },
    mounted: function () {
        console.log("name", this.name)
        console.log("el", this.el)
    }
})

3.6 组件共享方法

(对于methods可以重复使用)

<div id="app">
    <my-header></my-header>
</div>
<script>
    let mixs = {
        methods: {
            mouthEnter: function () {
                console.log("鼠标移入");
            },
            mouthLeave: function () {
                console.log("鼠标移出");
            }
        }
    };
    let Header = {
        template: `
            <div><button v-on:mouseenter="mouthEnter">鼠标移入</button>
            <button v-on:mouseleave="mouthLeave">鼠标移出</button></div>
        `,
        mixins: [mixs,]
    };

    let vm = new Vue({
        el: "#app",
        components: {
            "my-header": Header,
        }
    })
</script>

3.7 设置组件插槽

用于接收标签体中的数据

<div id="app">
    <my-header>a</my-header>  <!-- 数据为a -->
    <my-header>b</my-header>
    <my-header>c</my-header>
</div>
<script>
    let Header = {
        template: `
            <div>
                <slot></slot>  // 这里设置组件的插槽,用于接收组件标签体中的数据
            </div>
        `
    };
    let vm = new Vue({
        el: "#app",
        components: {
            "my-header": Header,
        }
    })
</script>

四、Vue生命周期

4.1 beforeCreate

在实例初始化之后,此时数据data、事件methods、el标签都还没有。

4.2 created

Vue实例创建完成后调用,此时数据data、事件methods都已经有了,但是this.$el还没有。

4.3 beforeMount

在挂载之前调用,此时this.$el已经有了,但是数据仍然以{{}}的形式预埋在标签中,还未渲染。

4.4 mounted

此时数据data、事件methods、this.$el都已经有了,并且已经渲染并展示完成。

4.5 beforeUpdate

数据更新时调用。

4.6 updated

数据更新完成时调用。

4.7 activated

该钩子函数所在的组件,必须在keep-alive树中。keep-alive可以保存当前组件的状态(某个标签是否被点开,内容展开等),在下次激活时恢复该状态。activated用于被keep-alive缓存的组件激活时调用。

4.8 deactivated

该钩子函数所在的组件,必须在keep-alive树中。deactivated用于被keep-alive缓存的组件停用时调用。

4.9 补充知识点

Flex盒子模型

// 使盒子内部(例如ul)的对象都垂直水平居中展示
.el-menu {
    display: flex;
    align-items: center;
    justify-content: center;
}

五、路由系统

5.1 路由系统的实现原理

<script>
    // 表示当窗口发生变化时触发,URL中#后面的部分变化
    window.onhashchange = function () {
        switch (location.hash) { //变化成的内容
            case '#/login':
                document.getElementById('app').innerText='Login';
                break;
            case '#/home':
                document.getElementById('app').innerText='Home';
                break;
            default:
                document.getElementById('app').innerText='Default';
                break;
        }
    }
</script>

5.2 路由系统基本用法

<script>
		// 1 声明使用VueRouter
    // 2 定义组件
    // 3 将组件与VueRouter关联
    // 4 将VueRouter注册到Vue实例中

    // 在Vue根实例中使用VueRouter
    Vue.use(VueRouter);

    // 配置了3个组件
    let Home = {
        template: `<div>Home</div>`
    };

    let Login = {
        template: `<div>Login</div>`
    };

    let Register = {
        template: `<div>Register</div>`
    };

    // 将VueRouter和组件关联起来
    let router = new VueRouter({
        routes: [
            {
                name: 'home',  // 用来配置命名路由
                path: '/',
                component: Home
            },
            {
                name: 'login',
                path: '/login',
                component: Login
            },
            {
                name: 'register',
                path: '/register',
                component: Register
            },
        ]
    });

    // 将VueRouter注册到Vue标签中
    let vm = new Vue({
        router: router,
        el: "#app",
        // router-link会渲染成a标签,to变成href属性
        // router-view是组件到渲染出口
        // 注意使用命名路由时,to前面有冒号
        // 下面注释部分时
        template: `
            <div>
                <router-link :to="{name: 'home'}">Home</router-link>
                <router-link :to="{name: 'login'}">Login</router-link>
                <router-link :to="{name: 'register'}">Register</router-link>

                <!--<router-link to="/">Home</router-link>-->
                <!--<router-link to="/login">Login</router-link>-->
                <!--<router-link to="/register">Register</router-link>-->
                <router-view></router-view>
            </div>
        `
    });
</script>

5.3 路由系统带参数

使用路由系统时,会给路由附带相关参数,通过如下方式实现。

<div id="app"></div>
<script>
    Vue.use(VueRouter);
    let Home = {
        template: `
            <div>
                <h2>Home Page</h2>
            </div>
        `
    };
    let User1 = {
        template: `
            <div>
                <h2>User1</h2>
            </div>
        `
    };
    let User2 = {
        template: `
            <div>
                <h2>User2</h2>
            </div>
        `
    };

    let App = {
      	// 参数的值从data中获取,不需要写{{}},直接引用即可
        template: `
            <div>
                <router-link :to="{name: 'home'}">Home</router-link>
                <router-link :to="{name: 'user1', params: {uid: uid1 }}">User1</router-link>
                <router-link :to="{name: 'user2', query: {uid: uid2 }}">User2</router-link>
                <router-view></router-view>
            </div>
        `,
        data() {
            return {
                uid1: 13,
                uid2: 15
            }
        }
    };

    routes = [
        {name: "user1", path: "/user1/:uid", component: User1}, // /user1/1
        {name: "user2", path: "/user2", component: User2},  // /user2/uid=2
        {name: "home", path: "/", component: Home}
    ];
    let router = new VueRouter({
        routes: routes
    });

    let vm = new Vue({
        el: "#app",
        template: `<App></App>`,
        components: {
            App: App
        },
        router: router
    })
</script>

5.4 路由系统之子路由

<div id="app"></div>
<script>
    Vue.use(VueRouter);、
    let Home = {
        template: `
            <div>
                <h3>主目录</h3>
                <router-link :to="{name: 'child1'}">子目录1</router-link>
                <router-link :to="{name: 'child2'}">子目录2</router-link>
                <router-view></router-view>
            </div>
        `
    };
    let Child1 = {
        template: `
            <div>
                <h4>子目录1</h4>
            </div>
        `
    };
    let Child2 = {
        template: `
            <div>
                <h4>子目录2</h4>
            </div>
        `
    };
    let router = new VueRouter({
        routes: [
            {
              	// 能写都尽量写成name的形式
                name: "home", path: "/home", component: Home, children: [
                    {name: "child1", path: "child1", component: Child1},
                    {name: "child2", path: "child2", component: Child2}
                ]
            },
        ]
    });

    let App = {
        template: `
            <div>
                <router-link :to="{name: 'home'}">主目录</router-link>
                <router-view></router-view>
            </div>
        `
    };

    let vm = new Vue({
        el: "#app",
        router: router,
        template: `
            <App></App>
        `,
        components: {
            App: App,
        }
    })
</script>

5.5 子路由+重定向

<div id="app"></div>
<script>
    Vue.use(VueRouter);
    let Home = {
        template: `
            <div>
                <router-link :to="{name: 'login'}">登陆</router-link>
                <router-link :to="{name: 'go'}">购物</router-link>
                <router-link :to="{name: 'pay'}">支付</router-link>
                <router-view></router-view>
            </div>
        `
    };
    let Login = {
        template: `
            <div>
                <h4>登陆</h4>
            </div>
        `
    };
    let Go = {
        template: `
            <div>
                <h4>购物</h4>
            </div>
        `
    };
    let Pay = {
        template: `
            <div>
                <h4>支付</h4>
            </div>
        `
    };
    let App = {
        template: `
            <div>
                <router-link :to="{name: 'home'}">首页</router-link>
                <router-view></router-view>
            </div>
        `
    };
    let router = new VueRouter({
      	// 这里的history用于将URL中的#去掉
      	mode: "history",
        routes: [
            {
                name: "home", path: "/", component: Home, children: [
                    {name: "login", path: "login", component: Login},
                    {name: "go", path: "go", component: Go},
                  	// 在这里配置重定向,实现跳转
                  	// 也可以使用命名路由 redirect: {name: "xx", params: {key: value}}
                    {name: "pay", path: "pay", component: Pay, redirect: "login"},
                ]
            }
        ]
    });
    let vm = new Vue({
        el: "#app",
        template: `
            <App></App>
        `,
        router: router,
        components: {
            App: App
        }
    })
</script>

5.6 路由系统之钩子函数

// 其他部分可以参考5.5代码,仅变更如下部分
let router = new VueRouter({
  			mode: "history",
        routes: [
            {
                name: "home", path: "/", component: Home, children: [
                    {name: "login", path: "login", component: Login},
                    {name: "go", path: "go", component: Go},
                  	// 在这里设置了钩子函数,用于校验是否需要登录状态
                    {name: "pay", path: "pay", component: Pay, meta: {require_login: false}},
                ]
            }
        ]
    });
		// 路由钩子函数,to表示计划去哪个页面,from表示当前页面,next表示实际跳转的页面URL
    router.beforeEach(function (to, from, next) {
        console.log(to, from, next);
        if (to.meta.require_login) {
          	// 如果是子路由的话,设置了name那么只需要写name对应的名称
            next("login")
        } else {
            next()
        }
    });

5.7 手动路由

this.$router.push("/")

5.8 路由中的注意点

this.$route 表示存放路由所有信息的对象
this.$router 表示VueRouter的实例化对象

六、Vuex

主要用来存放组件间通信的数据,可以看成是一个仓库

6.1 安装vuex

进入vue工作目录,执行 "npm install vuex"本地安装,查看package.json确认安装成功。

6.2 vuex的简单用法

// 在main.js中引入vuex,这里没有采用解耦的方式

Vue.use(Vuex)

// 实例化一个Vuex.Store对象,如果是解耦的话,"export default new Vuex.Store({})"
// 写在src目录下新建一个目录
let store = new Vuex.Store({
  // 调用方式: this.$store.state.name
  // 这里的 this.$store this为Vue实例,$store是 Vue实例的属性
  state: {
    name: "zimsky"
  },
  // getters 用来做数据的过滤与处理
  getters: {
    new_name: function (state) {
      return state.name + "- shenzhen"
    },
    // 这里仍然需要传入state,因为getters.new_name中用到了
    another_name: function (state, getters) {
      return getters.new_name + "- China"
    }
  },
  // 用mutations来处理组件发送的事件
  mutations: {
    // 这里用到了state的数据,所以要用state
    change_data: function (state, data) {
      state.name = data
    }
  }
})

let vm = new Vue({
  el: "#app",
  store,
  ...
})
// Home.vue组件中使用Vuex的数据

<template>
  <div>
    <h2>这是首页</h2>
    <h4>{{new_name}}</h4>
    <h2>{{name1}}</h2>
    <button v-on:click="myClick">点击更改name1数据</button>
  </div>
</template>

<script>
  export default {
    name: "Home",
    data() {
      return {
        // 这里的 name1 无法实时渲染数据,因为首次计算后获取到值,就不会再次使用this.$store.state.name这个函数来计算重新获取
        // name1: this.$store.state.name,
        new_name: this.$store.getters.another_name
      }
    },
    methods: {
      myClick: function (event) {
        // 向vuex中提交事件,第一个参数是事件函数,后面的参数是data值
        this.$store.commit("change_data", "Zimskyzeng")
      }
    },
    computed: {
      // 重点: 这里使用computed属性数据,能够根据 $store 中的数据变化实时更新 name1 的值
      // computed核心: 数据的即时计算,如果这里的 name1 放在data属性中,则无法实时渲染新数据
      name1: function () {
        return this.$store.state.name
      }
    }
  }
</script>

七、axios

这个axios是Vue中用来发送ajax请求的包

7.1 安装axios

npm install axios

7.2 axios的简单用法

// 在main.js中注册axios
// 注意: 由于axios不是Vue内置的,需要使用原型链来注册到Vue对象中
import axios from 'axios'
Vue.prototype.$axios = axios
<!-- 在组件中使用axios发送ajax请求 -->
<script>
  export default {
    name: "Home",
    mounted(){
      // 在这里使用 request 方法发送ajax请求
      this.$axios.request({
        url: 'http://www.baidu.com',
        method: 'get'
        // then 是请求成功后调用的方法
      }).then(function (data) {
        console.log(data)
        // catch 是请求失败后调用的方法
      }).catch(function (data) {

      })
    }
  }
</script>

八、npm

本质上就是包管理工具,常用命令如下:

1.npm init -y 将当前目录被npm管理;

2.npm install PACKAGE 将包安装到当前目录下,如果加上-g则是装到全局,如果加版本号为package@1.1.1;

3.npm uninstall 卸载包;

webpack是用来打包的,将src目录下面的js打包成适合浏览器识别的语句(某些语句浏览器不支持)。

vue-cli自带了webpack

Vue注意总结

  1. JS对象名都不用加引号;
  2. 在实例化Vue的时候,components是复数形式;
  3. 实例化router时,里面的routes是复数形式;
  4. Vue实例中的data用的是对象来表示,组件的data是用函数来表示
posted @ 2022-03-13 11:11  ZimskyZeng  阅读(353)  评论(0编辑  收藏  举报