Vue-2 vue-cli,生命周期,computed,watch,自定义指令

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/FlowGuanEr/article/details/97919014

Vue-2 vue-cli,生命周期,computed,watch,自定义指令

vue-cli环境配置、项目安装、目录树

我们使用webpack也可以手动安装配置vue脚手架环境,但是有一套更简单一点的vue-cli的构建工具,大大降低了webpack的使用难度。

一、配置vue的脚手架环境

npm install -g vue-cli

完成之后查看vue版本:

vue -V

如果终端承认vue命令,那么脚手架安装成功,否则要么脚手架安装失败,要么就是需要配置vue的path变量。

二、生成项目模板

1. 查看所有vue命令

vue -h

运行结果:

Options:
  -V, --version  output the version number
  -h, --help     output usage information

Commands:
  init           generate a new project from a template
  list           list available official templates
  build          prototype a new project
  create         (for v3 warning only)
  help [cmd]     display help for [cmd]

2. 构建vue项目

vue init webpack my-pro

在安装过程中,会出现以下一些选择:

? Project name  输入项目名称
? Project description 输入项目描述

? Author 作者

? Vue build 打包方式,回车就好了

? Install vue-router?  选择  Y 使用 vue-router,输入 N 不使用

? Use ESLint to lint your code? 代码规范,推荐 N

? Setup unit tests with Karma + Mocha? 单元测试,推荐 N

? Setup e2e tests with Nightwatch? E2E测试,N

3. 项目的目录结构

├── README.md // 项目说明文档

├── node_modules // 依赖包目录

├── build // webpack相关配置文件(都已配置好,一般无需再配置)

│       ├── build.js  //生成环境构建

│       ├── check-versions.js  //版本检查(node,npm)

│       ├── dev-client.js    //开发服务器热重载 (实现页面的自动刷新) 

│       ├── dev-server.js    //构建本地服务器(npm run dev)

│       ├── utils.js         // 构建相关工具

│       ├── vue-loader.conf.js   //csss 加载器配置

│       ├── webpack.base.conf.js    //webpack基础配置

│       ├── webpack.dev.conf.js     // webpack开发环境配置

│       └── webpack.prod.conf.js     //webpack生产环境配置

├── config // vue基本配置文件(可配置监听端口,打包输出等)

│       ├── dev.env.js // 项目开发环境配置

│       ├── index.js //   项目主要配置(包括监听端口、打包路径等)

│       └── prod.env.js // 生产环境配置

├── index.html // 项目入口文件

├── package-lock.json // npm5 新增文件,优化性能

├── package.json // 项目依赖包配置文件
├── src // 项目核心文件(存放我们编写的源码文件)

│       ├── App.vue // 根组件
        
│       ├── assets // 静态资源(样式类文件、如css,less,和一些外部的js文件)

│       │       └── css  //样式

│       │       └── font  //字体
│       │       └── images  //图片
│       ├── components // 组件目录

│       │       └── Hello.vue // 测试组件

│       ├── main.js // 入口js文件

│       └── router // 路由配置文件夹

│       └── index.js // 路由配置文件

└── static // 静态资源目录(一般存放图片类)

注:assets和static文件夹的区别
assets目录中的文件会被webpack处理解析为模块依赖,只支持相对路径形式。例如,在 <img src="./logo.png">background: url(./logo.png)中"./logo.png" 是相对的资源路径,将由Webpack解析为模块依赖。

static/ 目录下的文件并不会被Webpack处理:它们会直接被复制到最终的打包目录(默认是dist/static)下。必须使用绝对路径引用这些文件,这是通过在 config.js 文件中的 build.assetsPublicPathbuild.assetsSubDirectory 连接来确定的。

任何放在 static/ 中文件需要以绝对路径的形式引用:/static/[filename]

在我们实际的开发中,总的来说:static放不会变动的文件 assets放可能会变动的文件。

4. 安装好了之后启动项目

npm run dev

Component的封装、调用

vue-cli开发的是单页面的应用,只有一个 html 文件,在开发时,我们将 整个项目按功能划分成模块,每个模块都可以被任意的复用。高效率,低耦合。

模块的具体呈现形式就是组件,一个模块就是一个组件,每个组件有自己的数据、函数、周期等。

项目默认安装好了之后,可以在 src 文件夹中看到有一个默认的HelloWorld.vue文件。这就是默认安装的组件,一个组件就是一个 vue 文件。

我们在 src 的 components 文件夹中创建组件。

一、组件的基本构成

<template>
    <div class="home">
        <!-- 当前组件中的结构 -->
    </div>
</template>
<script>
    // 当前组件的脚本
    export default {
        // 当前组件的名称
        name: 'Home',
        data () {
            return {
                // 当前组件中所有的数据
            }
        },
        methods: {
            // 当前组件中所有的函数
        }
    }
</script>
<style>
    /* 当前组件的样式 */
</style>

注:

  • 组件中的结构必须包含在 <template>标签中。
  • 组件所有的子结构都必须包含在一个父级标签中,建议使用block标签。
  • 组件的<style>标签中的样式,是全局的样式,如果这个组件被其他组件调用,那么这个组件中的style样式会作用于当前页面中的所用同选择器元素。
  • <style>标签加了scoped(如<style scoped></style>)属性之后,其样式就是局部的,只作用于当前组件。

二、组件的调用

组件的调用方式有多种,作为子组件被调用,路由调用,插槽调用等,在这里我们先说传统的调用方式,即作为子组件被调用。

调用组建时,需要这么几个步骤:

  • 导出当前组件,exprot default {}
  • 在要使用当前组件的父级组件中,导入当前组件。
  • 将当前组件传入父组件的components中。
  • 在父组件中使用标签形式调用组件。
<!-- App.vue -->
<template>
  <div id="app">
    <!-- 
        在App.vue中调用Home.vue
        注: App.vue是根组件,它是和index.html有直接关系,在main.js中被实例化
     -->
    <Home/>
  </div>
</template>

<script>
// 导入Home.vue
import Home from './components/home'
export default {
  name: 'App',
  components: {
    // 在此声明  
    Home
  }
}
</script>

<style></style>

生命周期钩子函数

每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等,我们把这一系列的过程称为组件的生命周期(组件从注册到销毁的整个过程)。我们有时候需要在组件生命周期的某个过程中,执行某些代码,基于此,vue 提供了生命周期钩子函数,给了用户在不同阶段添加自己的代码的机会。

但是在此之前,我们要详细的介绍下组件的声明周期,以及生命周期中每个阶段组件完成和未完成的部分。

一、组件的生命周期

从图中可以看到,vue 为生命周期提供了 8 个钩子函数:

  • beforeCreate: 创建前
  • created: 创建后
  • beforeMount: 挂载前
  • mounted: 挂载后
  • beforeUpate: 更新前
  • upated: 更新后
  • beforeDestoy: 销毁前
  • destoyed: 销毁后

二、每个钩子函数执行时的组件形态

我们可以在组件的生命周期钩子函数中执行一些代码。

<template>
  <div id="app">
    <button @click="flag = !flag">点击</button>
    <Home v-if="flag"/>
  </div>
</template>

<script>
import Home from './components/home'
export default {
    name: 'App',
    components: {
        Home
    },
    data() {
        return {
            flag: true,
            info: 'i am info of component'
        }
    },
    //数据没有 元素没有
    beforeCreate:function(){
        console.log("创建前========");
        console.log(this.info);
        console.log(this.$el);
    },
    //数据有 元素没有
    created:function(){
        console.log("已创建========");
        console.log(this.info);
        console.log(this.$el);
    },
    //数据有 元素有 虚拟dom
    beforeMount:function(){
        console.log("mount之前========");
        console.log(this.info);
        console.log(this.$el);
    },
    //数据有 元素有,真实dom
    mounted:function(){
        console.log("mounted========");
        console.log(this.info);
        console.log(this.$el);
    },
    beforeUpdate:function(){
        console.log("更新前========");
        console.log(this.flag);
        console.log(this.$el);
    },
    updated:function(){
        console.log("更新完成========");
        console.log(this.flag);
        console.log(this.$el);
    },
    beforeDestroy:function(){
        console.log("销毁前========");
        console.log(this.info);
        console.log(this.$el);
    },
    destroyed:function(){
        console.log("已销毁========");
        console.log(this.info);
        console.log(this.$el);
    }
}
</script>

<style></style>

注:

  • 因为 vue 是通过控制数据来控制页面元素的,所以当数据发生变化时,页面中的元素也会随之响应,所以更新发生在数据变化时。beforeUpdate时,数据和节点都已经更新了。
  • 可以在 beforeCreate 中,执行一些loading效果。
  • created中结束loading,还可以做一些初始化操作,可以将一些页面刚开始加载就执行的代码放在这里,但是要注意,这个时候是没有节点的。
  • mounted:在这发起axios请求,拿回数据,配合路由钩子做一些事情,这个时候有节点又有数据,可以实例化一些插件效果。
  • beforeDestory: destoryed :当前组件已被删除,清空相关内容。

computed和watch

computed(计算数据)watch(观察者) 是 vue 组件中非常重要的两个功能。

一、computed

创建一个组建时,组件中的数据都声明在 data 中,但是有些数据需要依赖 data 中的其他数据计算,这个数据会随着依赖数据的变化而变化(比如购物车中被选中的商品总价,需要通过购物车中的商品列表计算,当购物车中商品发生变化,这个总价也会变化)。

我们把这种需要依赖其他变量进行处理之后产生的数据都声明在computed(计算属性)中。 计算属性允许我们对指定的视图,复杂的值计算。这些值将绑定到依赖项值,只在需要时更新。

计算属性示例:

<template>
  <div class="home">
    count: <input type="number" v-model.number="current.count">
    price: <input type="number" v-model.number="current.price">
    <button @click="addGood">添加</button>
    <h3>{{ sum }}</h3>
  </div>
</template>

<script>
export default {
    name: "Home",
    data() {
        return {
            current: {
                count: 0,
                price: 0
            },
            list: [{
                count: 3,
                price: 39.79
            },{
                count: 1,
                price: 109.34
            },{
                count: 3,
                price: 0.45
            }]
        }
    },
    methods: {
        addGood() {
            this.list.push(this.current)
        }
    },
    computed: {
        sum() {
            let sum_ = 0;
            for(let l of this.list) {
                sum_ += l.count * l.price;
            }
            return sum_;
        }
    }
}
</script>

注:
computed和methods有时候从代码角度而言,没有区别,但是底层的机制是完全不同的。
methods中的函数,在每次使用这个数据时都要调用,computed依赖于data中的数据,只有在它的相关依赖数据发生改变时才会重新求值,如果依赖数据不变,计算属性会缓存这个数据上次的值,不会重复调用复杂的计算过程。

官方文档反复强调对于任何复杂逻辑,都应当使用computed计算属性

computed和methods的区别示例:

<template>
  <div class="home">
    <button @click="n++">a变化</button>
    <button @click="m++">b变化</button>
    <h3>{{ sum_of_c }}</h3>
    <h3>{{ sum_of_c }}</h3>
    <h3>{{ sum_of_c }}</h3>
    <hr>
    <h3>{{ sum_of_m() }}</h3>
    <h3>{{ sum_of_m() }}</h3>
    <h3>{{ sum_of_m() }}</h3>
  </div>
</template>

<script>
export default {
    name: "Home",
    data() {
        return {
            n: 0,
            m: 0
        }
    },
    methods: {
        sum_of_m() {
            console.log('methods中的sum执行了');
            return this.n + this.m;
        }
    },
    computed: {
        sum_of_c() {
            console.log('computed中的sum执行了');
            return this.n + this.m;
        }
    }
}
</script>

二、watch

有时候我们需要在 data 中的数据发生变化时做一些操作,那就需要监听这个数据的变化,vue 提供了 watch(侦听器),来监听数据的变化。

computed 计算依赖于某些数据的值。watch 监听数据本身。

虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。

方式一:

<template>
  <div class="test2">
    <button @click="num = 1000">点击</button>
  </div>
</template>

<script>
    export default {
        name: "Test2",
        data() {
            return {
                num: 100
            }
        },
        watch: {
            num(newVal, oldVal) {
                console.log(newVal, oldVal);
            }
        }
    }
</script>

这种写法也可以改写成这样:

// ...
methods: {
    numChange(newVal, oldVal) {
        console.log(newVal, oldVal);
    }
},
watch: {
    num: 'numChange'
}
// ...

方式二:
方式一使用 watch 时有一个特点,就是当值第一次绑定的时候,不会执行监听函数,只有值发生改变才会执行。如果我们需要在最初绑定值的时候也执行函数,则就需要用到immediate属性。

// ...
watch: {
    num: {
        handler(newVal, oldVal) {
            console.log(newVal, oldVal)
        },
        // immediate表示在watch中首次绑定的时候,是否执行handler,值为true则表示在watch中声明的时候,就立即执行handler方法,值为false,则和一般使用watch一样,在数据发生变化的时候才执行handler。
        immediate: true
    }
}
// ...

watch监听对象时,对象的属性和属性的属性发生变化时,默认不会触发对于这个对象的监听。想要在对象的任意属性(无论嵌套多复杂)改变时,执行对象的watch函数,需要开启deep。

watch: {
    obj: {
        handler(newVal, oldVal) {
            // code
        },
        deep: true
    }
}
// ...

只是想监听对象的某个属性的变化:

watch: {
    'obj.key': {
        handler(newVal, oldVal) {
            // code
        }
    }
}
// ...

自定义指令

除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令。注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,Vue提供了一种更简单的方式-自定义指令,方便我们对DOM的操作。

自定义指令声明之后的调用方式和使用方式和 Vue 提供的默认指令一样。

一、自定义指令的小示例:

全局的自定义指令,在 main.js 中创建

Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时
  inserted: function (el) {
    // 聚焦元素
    el.focus();
  }
});

局部的自定义指令,创建在组件中:

export default {
    name: 'Home',
    directives: {
        focus: {
            // 指令的定义
            inserted: function (el) {
                el.focus()
            }
        }
    }
}

无论是局部的还是全局的,调用方式都相同,只是全局的指令可以被任意组件调用,局部的指令只能被当前组件调用:

<input v-focus type="text">

二、钩子函数和其参数

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

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

指令钩子函数会被传入以下参数:

  • el:指令所绑定的元素,可以用来直接操作 DOM 。
  • binding:一个对象,包含以下属性:
    • name:指令名,不包括 v- 前缀。
    • 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 }。
  • vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
  • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

更加具体的示例:

Vue.directive('demo', {
    bind: function (el, binding, vnode) {
        var s = JSON.stringify
        el.innerHTML =
        'name: '       + s(binding.name) + '<br>' +
        'value: '      + s(binding.value) + '<br>' +
        'expression: ' + s(binding.expression) + '<br>' +
        'argument: '   + s(binding.arg) + '<br>' +
        'modifiers: '  + s(binding.modifiers) + '<br>' +
        'vnode keys: ' + Object.keys(vnode).join(', ');
    }
})

调用:

<template>
  <div class="test3" v-demo:foo.a.b="message"></div>
</template>

<script>
    export default {
        name: "Test3",
        data() {
            return {
                message: 'hello!'
            }
        }
    }
</script>

注:

  • 指令的参数可以是动态的。例如,在 v-mydirective:[argument]=“value” 中,argument 参数可以根据组件实例数据进行更新!这使得自定义指令可以在应用中被灵活使用。
  • 在很多时候,你可能想在 bind 和 update 时触发相同行为,而不关心其它的钩子。比如这样写:
Vue.directive('color-swatch', function (el, binding) {
    el.style.backgroundColor = binding.value;
})
  • 如果指令需要多个值,可以传入一个 JavaScript 对象字面量。记住,指令函数能够接受所有合法的 JavaScript 表达式。
Vue.directive('demo', function (el, binding) {
    console.log(binding.value.color) // => "white"
    console.log(binding.value.text)  // => "hello!"
})
<div v-demo="{ color: 'white', text: 'hello!' }"></div>

练习: 封装v-on指令

参考文档
Vue中的computed
Vue中computed和methods中的区别
Vue中的watch详解

猜你喜欢

转载自blog.csdn.net/FlowGuanEr/article/details/97919014