美文网首页
vue官网重温笔记

vue官网重温笔记

作者: 拾钱运 | 来源:发表于2020-04-02 18:05 被阅读0次

    热重载:(hot-reloading)你修改页面之后,自动刷新,不用手动刷新

    vue-cli的配置参考 (待仔细学习)

    学习传送门:https://cli.vuejs.org/zh/config/#%E5%85%A8%E5%B1%80-cli-%E9%85%8D%E7%BD%AE

    webpack配置(待仔细学习)

    学习传送门:https://webpack.js.org/configuration/

    过渡&动画 (要仔细学习,又是一个门)

    学习传送门:https://cn.vuejs.org/v2/guide/transitions.html

    通过插槽分发内容、动态组件、解析Dom模板时的注意事项

    学习传送门:https://cn.vuejs.org/v2/guide/components.html#%E9%80%9A%E8%BF%87%E6%8F%92%E6%A7%BD%E5%88%86%E5%8F%91%E5%86%85%E5%AE%B9

    用key管理可复用的元素

    image.png

    切换input的时候,input就是独立的了

    v-if vs v-show

    传送门:https://www.jianshu.com/p/2feb8cad6abf

    对象变更检测注意事项

    var vm=new Vue({
      data:{
        userProfile:{
         name:'anika'
       }
      }
    })
    //如果想要给userProfile添加一个属性age,操作如下
    Vue.set(vm.userProfile,'age',27)
    或者
    vm.$set(vm.userProfile,'age',27)
    

    如果想要给已知的对象赋值多个属性可以这样操作

    Object.assign({},vm.userProfile,{
      age:27,
      favoriteColor:'vue Green'
    })
    

    计算属性的应用

    <li v-for="n in  evenNumber"> {{n}}</li>
    
    <script>
      new vue({
        data():{
          numbers:[1,2,3,4],
          computed:{
            evenNumber:function(){
                  return this.numbers.filter(function(number){
                      return   number%2===0
                  })
            }  
          }
        }
        
        })
    </script>
    

    或者

      <li v-for="n in even(numbers)">{{ n }}</li>
    
      data: {
          numbers: [ 1, 2, 3, 4, 5 ]
      },
      methods: {
        even: function (numbers) {
            return numbers.filter(function (number) {
                return number % 2 === 0
            })
        }
      }
    

    v-for在组件中的应用

    <ul>
       <li
         is="todo-item"
         v-for="(todo, index) in todos"
         v-bind:key="todo.id"
         v-bind:title="todo.title"
         v-on:remove="todos.splice(index, 1)"
       ></li>
     </ul>
    

    因为li时ul内部的有效元素,所以通过is="todo-item"的方式在ul中去循环
    这样循环,组件内部中需要通过prop去接收item传递的值:例如:

    Vue.component('todo-item', {
      template: '\
        <li>\
          {{ title }}\
          <button v-on:click="$emit(\'remove\')">Remove</button>\
        </li>\
      ',
      props: ['title']
    })
    

    事件修饰符

    传送门:https://cn.vuejs.org/v2/guide/events.html

    .stop阻止单击事件继续传播 可以阻止冒泡行为
    <div v-on:click="doThis1" class="btn">外部元素
                 <div class="s-btn" v-on:click.stop="doThis">
                     内部元素
                 </div>
             </div>
    <script>
       export default{
           name:'test',
           methods:{
            doThis(){
               console.log('我是内部元素') 
            },
            doThis1(){
               console.log('我是外部元素') 
            }
           }
       }
    </script>
    
    .capture修饰符事件捕获模式

    如果有此修饰符会先再此先处理,然后内部的监听事件才会进行处理
    例如:

     <div v-on:click.capture="doThis2" class="btn3">
                 1366777777777777777
                 <div class="btn2" v-on:click="doThis3">
                     134
                 </div>
             </div>
           doThis2(){
               console.log('我是1') 
            },
            doThis3(){
               console.log('我是2') 
            }
    

    输出结果为:由外向内


    image.png

    反之:如果没有.capture修饰符输出结果为:由内向外


    image.png
    .self只有当点击的是当前本身的时候才会触发click事件

    例如:
    代码如下: other在self内部,当点击self的时候,输出结果为

      <div v-on:click.capture="doThis2" class="btn3">
                 1366777777777777777
                 <div class="btn2" v-on:click="doThis3">
                     134
                     <div v-on:click.self="doThat">
                         self
                        <div v-on:click="doThat1">
                            other
                        </div>
                     </div>
                 </div>
             </div>
    
    image.png

    没有self

    注意:使用顺序,

    例如:v-on:click.prevent.self 会阻止所有的点击,
    v-on:click.self.prevent会阻止对元素自身的点击

    .passive修饰符尤其能够提供移动端的性能

    <div class="click"  v-on:scroll.passive="onScroll" >
     </div>
    

    不会等待scroll监听器执行完再去执行Onscroll,他会立即执行,因为.passive永远不会调用阻止默认行为的方法event.preventDefault()

    表单输入绑定

    注意

    1.input中的v-mobel这样绑定,不会在输入法组合文字的过程中更新例子如下:
    如果想要即时更新的,需要使用@input去实现

    <input v-model="message" placeholder="edit me">
    
    image.png

    2.在textarea文本域中

     (<textarea>{{text}}</textarea>) //并不会生效
    //应该使用v-mobel替换
    <textarea v-model="message" placeholder="add multiple lines"></textarea>
    

    3.select选择框


    image.png
    如果v-mobel表达式的初始值未能匹配任何选项,<select>元素将被渲染为"未选中"状态。
    在ios中,这会使用户无法选择第一个选项。因为这样的情况下,ios不会触发change事件。因此,更推荐像上面这样提供一个值为空的禁用选项
    

    4.修饰符 .lazy

    <input v-model.lazy="msg" >只有在input失去焦点或者按回车的时候才会被赋值
    

    插槽的使用

    image.png

    动态组件&异步组件

    动态组件的应用:

    • 多标签的界面中
    • 可以运用keep-live标签来保持原来的状态
    • 实际应用中的商品详情页面,可能会有多种状态,拼团,抢购,默认,等可以通过动态组件实现,不用好多if语句判断了
    <keep-alive>
      <component v-bind:is="currentTabComponent"></component>
    </keep-alive>
    

    异步组件应用:

    去请求的时候


    image.png

    定义组件名

    • kebab-case
      -PasecalCase
      注意:在使用自定义组件的时候,最好使用kebab-case的方式

    基础组件的自动化全局注册:有些不解,希望有人指教

    prop

    注意:

    在子组件中通过props定义名字的使用的驼峰,在运用绑定传递的时候需要用kebab-case

    Vue.component('blog-post',{
      props:['postTitle'],
      templete:'<h3>{{postTitle}}</h3>'
    })
    //
    <blog-post  post-title="hello"></blog-post>
    

    prop类型

    当只在prop中写字符串的时候为数组类型
    prop['title','likes','isPublished','commentIds','author']
    当在prop中写类型的时候为对象类型
    prop{
      title:String,
      likes:Number,
      isPublished:Boolean,
      commentIds:Array,
      author:Object
    }
    

    prop的传递有静态类型和动态类型

    //静态类型
    <blog-post title="My journey with Vue"></blog-post>
    //动态类型
    <blog-post v-bind:title="post.title"></blog-post>
    
    

    当传递一个静态类型的数字、对象、布尔类型的时候需要通过v-bind方式传递,告诉vue这不是一个字符串,这是一个javascript表达式

    <blog-post v-bind:likes="42"></blog-post>
    <blog-post is-published></blog-post>//默认传递的是true
    <blog-post v-bind:is-published="false"></blog-post>
    ...
    

    单向数据流

    父级prop的更新会向下流动到子组件中,但是反过来不行。这样会防止子组件意外改变父级组件的状态。
    子组件改变父组件的值需要通过this.$emit('值',方法名)的方式更改,在组件中:方法名:父组件在定义一个方法名

    prop验证

    Vue.component('my-component', {
      props: {
        // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
        propA: Number,
        // 多个可能的类型
        propB: [String, Number],
        // 必填的字符串
        propC: {
          type: String,
          required: true
        },
        // 带有默认值的数字
        propD: {
          type: Number,
          default: 100
        },
        // 带有默认值的对象
        propE: {
          type: Object,
          // 对象或数组默认值必须从一个工厂函数获取
          default: function () {
            return { message: 'hello' }
          }
        },
        // 自定义验证函数
        propF: {
          validator: function (value) {
            // 这个值必须匹配下列字符串中的一个
            return ['success', 'warning', 'danger'].indexOf(value) !== -1
          }
        }
      }
    })
    

    注意:那些prop会在一个组件实例创建之前进行验证,所以实例属性如(data、computed等)再default或validator函数中不可用

    自定义事件

    .sync修饰符
    在有些情况下,我们可能需要对一个prop进行“双向绑定”。也就是子组件可以修改父组件的问题
    普通的this.$emit

    myShow(){
      this.$emit('fun','修改的值传递')
    }
    //父组件
     <mycom v-on:fun="change"></mycom>
    

    使用.sync修饰符

    closeDiv() {
              this.$emit('update:show', false); //触发 input 事件,并传入新值
            }
    //父组件
     <myComponent :show.sync='valueChild' style="padding: 30px 20px 30px 5px;border:1px solid #ddd;margin-bottom: 10px;"></myComponent>
    <button @click="changeValue">toggle</button>
    //当前中的valueChild就是子组件传递过来的值,不用父组件在创建方法接收  
    export default{
        data(){
            return{
                valueChild:true,
            }
        },
        methods:{
            changeValue(){
                this.valueChild = !this.valueChild
            }
        }
    }
    
    

    注意:.sync后面不能是表达式

    插槽:https://cn.vuejs.org/v2/guide/components-slots.html

    处理边界情况

    访问子组件实例,或子元素可以通过ref

    <base-input ref="usernameInput"></base-input>
    this.$refs.usernameInput
    

    注意:ref只会在组件渲染完成之后生效,并且他们不是响应式的。 要避免在模板或计算属性中使用refs

    依赖注入

    意思就是如果子元素想要访问父组件中的方法
    在父组件中

    provide: function () {
      return {
        getMap: this.getMap
      }
    }
    

    在子组件中

    inject: ['getMap']
    

    注意:provide 和 inject 主要在开发高阶插件/组件库时使用。并不推荐用于普通应用程序代码中。

    程序化的事件监听

    • $on(eventName,eventHandler)监听一个事件
    • $once(eventName,eventHandler) 一次性监听一个事件
    • $off(eventName,eventHander) 停止监听一个事件

    应用场景:
    使用第三方库的时候,例如时间插件

    //平常我们使用
    // 一次性将这个日期选择器附加到一个输入框上
    // 它会被挂载到 DOM 上。
    mounted: function () {
      // Pikaday 是一个第三方日期选择器的库
      this.picker = new Pikaday({
        field: this.$refs.input,
        format: 'YYYY-MM-DD'
      })
    },
    // 在组件被销毁之前,
    // 也销毁这个日期选择器。
    beforeDestroy: function () {
      this.picker.destroy()
    }
    
    
    潜在问题

    需要在在这个组件实例中保存这个picker
    难于程序化清理

    通过程序化监听器解决,并且可以引用使用多个时间三方库

    mounted: function () {
      var picker = new Pikaday({
        field: this.$refs.input,
        format: 'YYYY-MM-DD'
      })
    
      this.$once('hook:beforeDestroy', function () {
        picker.destroy()
      })
    }
    
    mounted: function () {
      this.attachDatepicker('startDateInput') //不同的ref  元素,使用picker
      this.attachDatepicker('endDateInput')
    },
    methods: {
      attachDatepicker: function (refName) {
        var picker = new Pikaday({
          field: this.$refs[refName],
          format: 'YYYY-MM-DD'
        })
    
        this.$once('hook:beforeDestroy', function () {
          picker.destroy()
        })
      }
    }
    

    组件之间的循环引用

    例子:

    <p>
      <span>{{ folder.name }}</span>
      <tree-folder-contents :children="folder.children"/>
    </p>
    

    tree-folder-contents 组件

    <ul>
      <li v-for="child in children">
        <tree-folder v-if="child.children" :folder="child"/>
        <span v-else>{{ child.name }}</span>
      </li>
    </ul>
    

    需要在注册组件的时候这样引入:使用webpack的异步import

    components: {
      TreeFolderContents: () => import('./tree-folder-contents.vue')
    }
    

    组件之间循环引用的问题所在:(没看懂,请大佬指教)

    • 为了解释这里发生了什么,我们先把两个组件称为 A 和 B。模块系统发现它需要 A,但是首先 A 依赖 B,但是 B 又依赖 A,但是 A 又依赖 B,如此往复。这变成了一个循环,不知道如何不经过其中一个组件而完全解析出另一个组件。为了解决这个问题,我们需要给模块系统一个点,在那里“A 反正是需要 B 的,但是我们不需要先解析 B。”

    通过v-once 创建低开销的静态组件

    ue.component('terms-of-service', {
      template: `
        <div v-once>
          <h1>Terms of Service</h1>
          ... a lot of static content ...
        </div>
      `
    })
    

    注意:
    当你需要渲染大量静态内容时,极少数的情况下它会给你带来便利,除非你非常留意渲染变慢了,不然它完全是没有必要的——再加上它在后期会带来很多困惑。例如,设想另一个开发者并不熟悉 v-once 或漏看了它在模板中,他们可能会花很多个小时去找出模板为什么无法正确更新。

    混入

    例子:

    var mixin = {
      data: function () {
        return {
          message: 'hello',
          foo: 'abc'
        }
      }
    }
    
    new Vue({
      mixins: [mixin],
      data: function () {
        return {
          message: 'goodbye',
          bar: 'def'
        }
      },
      created: function () {
        console.log(this.$data)
        // => { message: "goodbye", foo: "abc", bar: "def" }
      }
    })
    
    遇到冲突的data变量和方法名的策略
    • 同名的data变量优先选择组件内部的
    • 同名的钩子函数会合并成一个数组例如created 都会执行
    var mixin = {
      created: function () {
        console.log('混入对象的钩子被调用')
      }
    }
    
    new Vue({
      mixins: [mixin],
      created: function () {
        console.log('组件钩子被调用')
      }
    })
    
    // => "混入对象的钩子被调用"
    // => "组件钩子被调用"
    
    • 值为对象的选项,例如 method、components、 directives 将被合并为同一个对象。两个兑现键值对冲突的时候优先组件内部的
    var mixin = {
      methods: {
        foo: function () {
          console.log('foo')
        },
        conflicting: function () {
          console.log('from mixin')
        }
      }
    }
    
    var vm = new Vue({
      mixins: [mixin],
      methods: {
        bar: function () {
          console.log('bar')
        },
        conflicting: function () {
          console.log('from self')
        }
      }
    })
    
    vm.foo() // => "foo"
    vm.bar() // => "bar"
    vm.conflicting() // => "from self"
    

    过滤器

    <!-- 在双花括号中 -->
    {{ message | capitalize }}
    
    <!-- 在 `v-bind` 中 -->
    <div v-bind:id="rawId | formatId"></div>
    

    自定义过滤器:

    filters: {
      capitalize: function (value) {
        if (!value) return ''
        value = value.toString()
        return value.charAt(0).toUpperCase() + value.slice(1)
      }
    }
    

    或者

    Vue.filter('capitalize', function (value) {
      if (!value) return ''
      value = value.toString()
      return value.charAt(0).toUpperCase() + value.slice(1)
    })
    
    new Vue({
      // ...
    })
    

    当全局过滤器和局部过滤器重名时,采用局部过滤器

    //过滤器可以串联
    {{ message | filterA | filterB }}
    
    //先将message传入到filterA方法中,然后再把筛选后的结果传入到filterB方法中进行筛选
    

    还可以这样:
    因为过滤器是javascript函数,因此可以接收参数

    {{ message | filterA('arg1', arg2) }}
    

    单元测试 (不明白,需要学习)

    安全

    第一原则:永远不要使用不可信任得模板
    这样得做就等于允许在应用程序中执行任意得javascript,更糟得是如果在服务端渲染得话可能导致服务器被攻破。
    例子:

    new Vue({
      el: '#app',
      template: `<div>` + userProvidedString + `</div>` // 永远不要这样做
    })
    

    vue的安全措施:https://cn.vuejs.org/v2/guide/security.html

    Html内容

    不论使用模板还是渲染函数,内容都会被自动转义。
    例如:

    <h1>{{ userProvidedString }}</h1>
    

    如果userProvideString变量中包含了

    '<script>alert("hi")</script>' //被注入的恶意代码
    

    则会被转义成

    &lt;script&gt;alert(&quot;hi&quot;)&lt;/script&gt;  //没有办法执行script了
    

    因此避免了脚本注入。该转义通过诸如textContent的浏览原生的Api完成,所以除非浏览器本身存在安全漏洞,否则不会存在安全漏洞

    Attribute(属性)绑定 道理同
    <h1 v-bind:title="userProvidedString">
      hello
    </h1>
    

    如果 userProvidedString 包含了:

    '" onclick="alert(\'hi\')'//恶意代码
    

    则会被转义成为如下 HTML:

    &quot; onclick=&quot;alert('hi')
    

    潜在危险

    注入url
    <a v-bind:href="userProvidedUrl">
      click me
    </a>
    

    如果没有对url进行过滤“防止javascript:通过”则会由潜在的安全问题。有些库可以帮助解决
    sanitize-url
    https://www.npmjs.com/package/@braintree/sanitize-url

    注意:在前端进行了url过滤,那么久已经有安全问题了。用户提供的url永远需要通过后端在入库之前进行过滤。然后这个被注入的问题在每个客户端连接api时就会被阻止,包括原生移动应用。

    注入样式
    <a
      v-bind:href="sanitizedUrl"
      v-bind:style="userProvidedStyles"
    >
      click me
    </a>
    

    假设sanitizedUrl已经被过滤过了。但通过userProvidedStyles,恶意用户仍然可以提供css来进行“点击诈骗”。将链接的样式设置为一个透明的方框覆盖在“登陆”上,然后再把按钮做成应用中的按钮的样子。它们就可以获取一个用户真实的登陆信息。
    可以改为:

    <a
      v-bind:href="sanitizedUrl"
      v-bind:style="{
        color: userProvidedColor,
        background: userProvidedBackground
      }"
    >
      click me
    </a>
    

    推荐使用对象语法且只允许用户提供特定的可以安全控制的property的值

    深入响应式原理

    当把一个javascript对象传入vue实例作为data选项,vue将遍历此对象所有的属性,并使用Object.defineProperty把这些属性全部转换为getter/setter.Object.defineProoperty是es5中一个无法shim的特性。是他们能够让vue追踪依赖。再属性被访问和修改时通知变更。
    每一个实例都对应一个watcher实例,他会再组件渲染过程中把接触过的数据属性记录为依赖。之后当依赖的setter触发时,会通知watcher,从而使它关联的组件更新渲染。


    image.png

    检测变化的注意事项

    Vue不能检测数组和对象的变化。

    对象

    vue无法检测property的添加和移除。
    vue会在初始化实例时对属性执行getter/setter转化,所以属性必须在data上存在才能让vue将它转换为响应式。
    对于已经创建得vue实例,vue不允许动态添加根级别得响应式
    可以使用Vue.set(object,propertyName,value)
    例如:

    Vue.set(vm.someObject, 'b', 2)
    

    或者

    this.$set(this.someObject,'b',2)
    

    有的时候可能需要为已有对象赋值多个新属性,比如使用Object.assign()或_.extend() 。但是这样添加到对象上得新属性不会触发更新。没有被监听到,添加这些属性。可以这样,用原对象与要混合进去得对象得属性一起创建一个新的对象。

    this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
    
    数组

    vue不能检测的数组变动:
    1.当你利用索引直接设置一个数组项时:

    vm.items[indexOfItem] = newValue
    

    2.当你修改数组的长度时

    vm.items.length = newLength
    

    举例:

    var vm = new Vue({
      data: {
        items: ['a', 'b', 'c']
      }
    })
    vm.items[1] = 'x' // 不是响应性的
    vm.items.length = 2 // 不是响应性的
    

    可以触发更新的修改数组方式:

    Vue.set(vm.items, indexOfItem, newValue)||vm.$set(vm.items, indexOfItem, newValue)
    

    解决数组的长度

    vm.items.splice(newLength)
    

    异步更新队列

    vue在更新dom时是异步执行的。只要侦听到数据变化,vue将开启一个队列,vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个watcher被多次触发,只会被堆入到队列中一次。
    vue在内部对异步队列尝试使用原生的 Promise.then 、MutationObserver和setImmediate,如果执行环境不支持,则会采用setTimeout(fn,0)

    var vm = new Vue({
      el: '#example',
      data: {
        message: '123'
      }
    })
    vm.message = 'new message' // 更改数据
    vm.$el.textContent === 'new message' // false(这个时候还没更改完成)
    //只有在执行下一个事件循环tick中更新
    Vue.nextTick(function () {
      vm.$el.textContent === 'new message' // true
    })
    

    或者

    Vue.component('example', {
      template: '<span>{{ message }}</span>',
      data: function () {
        return {
          message: '未更新'
        }
      },
      methods: {
        updateMessage: function () {
          this.message = '已更新'
          console.log(this.$el.textContent) // => '未更新'
          this.$nextTick(function () {
            console.log(this.$el.textContent) // => '已更新'
          })
        }
      }
    })
    

    因为$necxtTick()返回一个Promise对象,所以你可以使用新的es7的语法糖
    async/await语法完成相同的事情
    async 声明一个异步的function
    await 等待一个异步方法执行完成 再去执行下面的代码

    methods: {
      updateMessage: async function () {
        this.message = '已更新'
        console.log(this.$el.textContent) // => '未更新'
        await this.$nextTick()  等待
        console.log(this.$el.textContent) // => '已更新'
      }
    }
    

    在mounted钩子函数默认是this.$el.document获取不到

    //可以这样
    mounted: function () {
      this.$nextTick(function () {
        // 代码保证 this.$el 在 document 中
      })
    }
    
    注意:

    在created钩子中通过$on绑定了事件,最好在组件销毁前,清除事件的监听

    beforeDestroy: function () {
      eventHub.$off('add-todo', this.addTodo)
      eventHub.$off('delete-todo', this.deleteTodo)
    },
    

    vue和其它框架的对比:https://www.jianshu.com/p/eef0a8e0dc49

    vue打包得时候总是出现好多js,希望只出现一个js

    因为页面比较多,打包成一个js的话,文件会很大。否则第一次加载的时候半天都是白屏,一般都是按需加载,打包也是根据需要进行打包

    相关文章

      网友评论

          本文标题:vue官网重温笔记

          本文链接:https://www.haomeiwen.com/subject/uzatphtx.html