继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Vue简单知识点(上)

侠客岛的含笑
关注TA
已关注
手记 133
粉丝 1.6万
获赞 1807

vue-loader

const docsLoader = require.resolve('./doc-loader')
module.exports = (isDev) => {
  return {
    preserveWhitepace: true, // 去除html中多余的空格
    extractCSS: !isDev, // 讲css单独打包到一个文件
    cssModules: {
      localIdentName: isDev ? '[path]-[name]-[hash:base64:5]' : '[hash:base64:5]', // 避免class命名冲突,而且可以定义class名称
      camelCase: true // 横杠式命名转化成小驼峰命名,帮助JS更容易解析
    },
    // hotReload: false, // 根据环境变量生成,热重载 (样式热重载是通过vue-style-loader来实现的,而不是vue-loader给component进行热重载的功能。但是html更新就是通过刷新更新)
    loaders: { // 给自定义的模块指定loader,更多用在给组件库指定文档的时候使用
	    'docs' : docsLoader,
	    'js': 'cooffe-loader' // 也可以给js,html,css指定不同的loader 
    },
    preLoader: { // 解析loader之前的解析,也就是先解析},
    postLoader: {// 之后解析}
  }
}
css属性添加module,然后调用的使用使用:class="$style.cssName" 其实这种使用方式相当于,
<script>
export default {
	computed: {
		$style() {
			return {
				calssName:''
			}
		}
	}
}
</script>
"precommit": "npm run lint-fix",

为了缩短 Lint 的反馈链条,把 Lint 挪到本地是最有效的办法。常见做法是使用 husky 或者 pre-commit 在本地提交之前做 Lint。不过要保证项目已经git init ,否则git hook无法生效

Vue Instance

  • $data
  • $props
  • $el
  • $options 当我创建vue对象所有的options,还有默认的options,但是不是同一个对象,也就是无法修改$options上的值,但是我们可以这样调用$options.render
  • root(app.root (app.root === app) 通过这种方式挂载
  • $chiildren
  • $slots 插槽
  • $scopedSlots 插槽
  • $refs 模板的引用,快速定位模板中的某一个节点或者是组件,如果是html,他会返html中的对象,如果是组件,返回实例
  • $isServer 判断服务端渲染
  • $watch
  • $once (多次调用$once)
  • $forceUpdate 强制组件重新渲染,小心使用,容易影响性能
  • $set 赋值 app.$set(app.obj, 'a', i)
  • $delete 删除元素
  • $nextTick 将回调延迟到下次DOM更新循环之后执行。调用callback
    • 更新值,但是DOM节点没有变化的时候调试
不过只有在下一次渲染的时候才会生效
app.$options.render = (h) => {
	return h('div',{},'new render function')
	}
const unWatch = app.$watch('text', (newText, oldText) => {
  console.log(`${newText} : ${oldText}`)
})
unWatch() 注销,防止内存溢出
不冒泡,必须绑定到同一个对象上
app.$once('test', (a, b) => {
  console.log(`test emited ${1} ${b}`)
})
app.$emit('test', 1, 2)

Vue lifecycle

  • beforeCreate和create肯定会执行(如果没有绑定节点或者是mounted,绑定节点 ),拿不到$this.$el
    • beforeCreate,事件OK,但是reactivity还没有ok
  • update beforeUpdate需要等到数据更新时执行
  • beforeDestory destroyed解除所有的事件监听和watch
  • renderError() 开发时更方便的进行调错
  • errorCapured 帮助我们收集线上的错误,可以冒泡
render (h) {
    throw new TypeError('render error')
  },
  renderError (h, err) {
    return h('div', {}, err.stack)
  },

data-binding

new Vue({
  el: '#root',
  // template: `
  //   <div :id="aaa" @click="handleClick">
  //     <p v-html="html"></p>
  //   </div>
  // `,
  template: `
    <div
      :class="[{ active: isActive }]"
      :style="[styles, styles2]"
    >
      <p>{{getJoinedArr(arr)}}</p>
    </div>
  `,
  data: {
    isActive: false,
    arr: [1, 2, 3],
    html: '<span>123</span>',
    aaa: 'main',
    styles: {
      color: 'red',
      appearance: 'none' // 自动加兼容前缀
    },
    styles2: {
      color: 'black'
    }
  },
  methods: {
    handleClick () {
      alert('clicked') // eslint-disable-line
    },
    getJoinedArr (arr) {
      return arr.join(' ')
    }
  }
})

computed and watch

  • computed相比方法的性能开销比较小
    • 有缓存
    • 只有依赖的值变化,他才会重新计算并且缓存起来
    • 不到万不得已,不要使用set方法
  • 改变变量时,整个DOM要重新渲染,如果我们使用了方法,该方法就会被重新调用,但是computed不会
  • watch 最初绑定的时候不会执行,当然可以使用handler+immediate:true解决
    • 适合监听数据变化,然后执行指定操作,但是不适用于显示某个数据,这个时候,computed就比较合适了
    • 当我们修改内部属性时,无法触发handler,但是我们加deep参数,深入观察,当然,这样性能开销就很大了
  • 不要在computed和watch 中修改你依赖的值,无限循环
import Vue from 'vue'

new Vue({
  el: '#root',
  template: `
    <div>
      <p>Name: {{name}}</p>
      <p>Name: {{getName()}}</p>
      <p>Number: {{number}}</p>
      <p>FullName: {{fullName}}</p>
      <p><input type="text" v-model="number"></p>
      <p>FirstName: <input type="text" v-model="firstName"></p>
      <p>LastName: <input type="text" v-model="lastName"></p>
      <p>Name: <input type="text" v-model="name"></p>
      <p>Obj.a: <input type="text" v-model="obj.a"></p>
    </div>
  `,
  data: {
    firstName: 'Jokcy',
    lastName: 'Lou',
    number: 0,
    fullName: '',
    obj: {
      a: 0
    }
  },
  computed: {
    name: {
      get () {
        console.log('new name')
        return `${this.firstName} ${this.lastName}`
      },
      set (name) {
        const names = name.split(' ')
        this.firstName = names[0]
        this.lastName = names[1]
      }
    }
  },
  watch: {
    'obj.a': {
      handler () {
        console.log('obj.a changed')
        this.obj.a += 1
      },
      immediate: true
      // deep: true
    }
  },
  methods: {
    getName () {
      console.log('getName invoked')
      return `${this.firstName} ${this.lastName}`
    }
  }
})

directive

new Vue({
  el: '#root',
  template: `
    <div>
      <div>Text: {{text}}</div>
      <div v-if="text === 0">Else Text: {{text}}</div>
      <div v-else>else content</div>
      <div v-html="html"></div>
      <input text="text" v-model="text">
      <input type="checkbox" v-model="active">
      <div>
        <input type="checkbox" :value="1" v-model="arr">
        <input type="checkbox" :value="2" v-model="arr">
        <input type="checkbox" :value="3" v-model="arr">
      </div>
      <div>
        <input type="radio" value="one" v-model="picked">
        <input type="radio" value="two" v-model="picked">
      </div>
      <ul>
        <li v-for="(item, index) in arr" :key="item">{{item}}:{{index}}</li>
      </ul>
      <ul>
        <li v-for="(val, key, index) in obj">{{val}}:{{key}}:{{index}}</li>
      </ul>
    </div>
  `,
  data: {
    arr: [2, 3],
    obj: {
      a: '123',
      b: '456',
      c: '789'
    },
    picked: '',
    text: 0,
    active: false,
    html: '<span>this is html</span>'
  }
})

  • v-on
  • v-bind
  • v-model 一般用在input等交互标签
    • 可以在后面加上number,trim,lazy等修饰符
    • v-pre不解析
    • v-cloak 防止页面加载时出现 vuejs 的变量名
    • v-once 数据只绑定一次,节省性能(不更新数据)
    • v-props 作为一个子节点不应该主动去修改主节点的值
  • 数据经常变化,如果每次变化,view会重新渲染列表,再把它放进Dom中去,性能开销就会比较大
  • :key减少性能开销,变化,如果在缓存列表中拿到同样的key,就把这行的dom节点复用了,就不用生成新的dom节点
  • 一般不推荐使用index,一般用item,唯一值

vue原生指令

  • v-show
  • v-html
  • v-text
  • v-if 如果只是单纯的控制隐藏显示,推荐使用v-show.因为v-if是会动态增删节点,性能差很多(Dom重绘和重新排版)
  • v-for="(item,index) in arr"
  • v-for="(val,key) in obj"
  • :key 数据中唯一ID值 因为数据经常变化,性能开销大 ,但是如果定义key之后,如果key没有变化,直接复用Dom节点,不建议用index做
  • @click
  • v-model.number .lazy(在 “change” 而不是 “input” 事件中更新)
  • v-pre
  • v-cloak
  • v-once 数据绑定内容只执行一次

component

const compoent = {
  props: {
    active: {
      // type: Boolean,
      // required: true,
      validator (value) {
        return typeof value === 'boolean'
      }
    },
    propOne: String
  },
  template: `
    <div>
      <input type="text" v-model="text">
      <span @click="handleChange">{{propOne}}</span>
      <span v-show="active">see me if active</span>
    </div>
  `,
  data () { // data必须是通过function定义,不能是全局的
    return {
      text: 0
    }
  },
  methods: {
    handleChange () {
      this.$emit('change')
    }
  }
}

// Vue.component('CompOne', compoent) 全局注册组件

new Vue({
  components: {
    CompOne: compoent
  },
  data: {
    prop1: 'text1'
  },
  methods: {
    handleChange () {
      this.prop1 += 1
    }
  },
  mounted () {
    console.log(this.$refs.comp1) 拿到vue的实例
  },
  el: '#root',
  template: `
    <div>
      <comp-one ref="comp1" :active="true" :prop-one="prop1" @change="handleChange"></comp-one>
      <comp-one :active="true" propOne="text2"></comp-one>
    </div>
  `
})

Component extend

const compoent = {
  props: {
    active: Boolean,
    propOne: String
  },
  template: `
    <div>
      <input type="text" v-model="text">
      <span @click="handleChange">{{propOne}}</span>
      <span v-show="active">see me if active</span>
    </div>
  `,
  data () {
    return {
      text: 0
    }
  },
  mounted () {
    console.log('comp mounted')
  },
  methods: {
    handleChange () {
      this.$emit('change')
    }
  }
}

const parent = new Vue({
  name: 'parent'
})

const componet2 = {
  extends: compoent,
  data () {
    return {
      text: 1
    }
  },
  mounted () {
    console.log(this.$parent.$options.name)
  }
}

// const CompVue = Vue.extend(compoent)

// new CompVue({
//   el: '#root',
//   propsData: { 
//     propOne: 'xxx'
//   },
//   data: { 合并或覆盖
//     text: '123'
//   },
//   mounted () { components mounted先被调用,然后该mounted被调用
//     console.log('instance mounted')
//   }
// })
// 第二种方式 ,可以在子组件内部通过$partent直接调用父组件(子组件必须是通过new出来的,而不是模板编译)
new Vue({
  parent: parent,
  name: 'Root',
  el: '#root',
  mounted () {
    console.log(this.$parent.$options.name)
  },
  components: {
    Comp: componet2
  },
  data: {
    text: 23333
  },
  template: `
    <div>
      <span>{{text}}</span>
      <comp></comp>
    </div>
  `
})

Component 自定义双向绑定

import Vue from 'vue'

const component = {
  model: {
    prop: 'value1',
    event: 'change'
  },
  props: ['value1'],
  template: `
    <div>
      <input type="text" @input="handleInput" :value="value1">
    </div>
  `,
  methods: {
    handleInput (e) {
      this.$emit('change', e.target.value)
    }
  }
}

new Vue({
  components: {
    CompOne: component
  },
  el: '#root',
  data () {
    return {
      value: '123'
    }
  },
  template: `
    <div>
      <comp-one v-model="value"></comp-one>
    </div>
  `
})
这里的<comp-one v-model="value"></comp-one>也可以这样写
<comp-one :value="value" @input="value = araguments[0]"></comp-one>

组件高级属性

import Vue from 'vue'

const ChildComponent = {
  template: '<div>child component: {{data.value}}</div>',
  inject: ['yeye', 'data'],
  mounted () {
    // console.log(this.yeye, this.value)
  }
}

const component = {
  name: 'comp',
  components: {
    ChildComponent
  },
  // template: `
  //   <div :style="style">
  //     <div class="header">
  //       <slot name="header"></slot>
  //     </div>
  //     <div class="body">
  //       <slot name="body"></slot>
  //     </div>
  //   </div>
  // `,
  template: `
    <div :style="style">
      <slot :value="value" aaa="111"></slot>
      <child-component />
    </div>
  `,
  data () {
    return {
      style: {
        width: '200px',
        height: '200px',
        border: '1px solid #aaa'
      },
      value: 'component value'
    }
  }
}

new Vue({
  components: {
    CompOne: component
  },
  provide () { // 解决跨层级关系,提供上下文的关系
    const data = {}
	// provide默认不提供Reactive (Hack方法)
    Object.defineProperty(data, 'value', {
      get: () => this.value,
      enumerable: true
    })

    return {
      yeye: this,
      data
    }
  },
  el: '#root',
  data () {
    return {
      value: '123'
    }
  },
  mounted () {
    console.log(this.$refs.comp.value, this.$refs.span)
  },
  template: `
    <div>
      <comp-one ref="comp">
        <span slot-scope="props" ref="span">{{props.value}} {{props.aaa}} {{value}}</span>
      </comp-one>
      <input type="text" v-model="value" />
    </div>
  `
})

Vue render

import Vue from 'vue'

const component = {
  props: ['props1'],
  name: 'comp',
  // template: `
  //   <div :style="style">
  //     <slot></slot>
  //   </div>
  // `,
  render (createElement) {
    return createElement('div', {
      style: this.style
      // on: {
      //   click: () => { this.$emit('click') }
      // }
    }, [
      this.$slots.header,
      this.props1
    ])
  },
  data () {
    return {
      style: {
        width: '200px',
        height: '200px',
        border: '1px solid #aaa'
      },
      value: 'component value'
    }
  }
}

new Vue({
  components: {
    CompOne: component
  },
  el: '#root',
  data () {
    return {
      value: '123'
    }
  },
  mounted () {
    console.log(this.$refs.comp.value, this.$refs.span)
  },
  methods: {
    handleClick () {
      console.log('clicked')
    }
  },
  // template: `
  //   <comp-one ref="comp">
  //     <span ref="span">{{value}}</span>
  //   </comp-one>
  // `,
  render (createElement) {
    return createElement(
      'comp-one',
      {
        ref: 'comp',
        props: {
          props1: this.value
        },
        // on: {
        //   click: this.handleClick
        // },
        nativeOn: {
          click: this.handleClick
        }
      },
      [
        createElement('span', {
          ref: 'span',
          slot: 'header',
          // domProps: {
          //   innerHTML: '<span>345</span>'
          // }
          attrs: {
            id: 'test-id'
          }
        }, this.value)
      ]
    )
  }
})

Vue Router

服务端渲染

图片描述

报错解决

图片描述

解决 https://github.com/vuejs/vue-loader/issues/470
npm install vue-template-compiler --save-dev
打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP