Vue3系统入门与项目实战学习笔记

7 篇文章 2 订阅
订阅专栏

文章目录


课程资源:链接:https://pan.baidu.com/s/1OhjafwGM48nny7lPDpC-6Q 提取码:qv0o

1 Vue 语法初探

1-1 课前须知

  • 如何使用vue完成项目的开发

  • 深度理解特性背后的原理


在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

1-2 初学编写 HelloWorld 和 Counter

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>hello world</title>
	<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="root"></div>
</body>
<script>
    // Vue.createApp({ // 创建vue实例
    //     template: '<div>hello world</div>' // 在root元素里面用此模板展示内容
    // }).mount('#root'); // 在id为root的元素上使用vue

    /**
     * 1.找到root节点,往上面挂一个元素
     * 2.元素的内容就是template里面的内容
     * 3.用{{}}}:表示这是js表达式,是一个变量
     *      <div id="root">
     *          <div>{{content}}</div>
     *      </div>
     */
    Vue.createApp({
        data() { // 定义数据
            return {
                content: 1
            }
        },
	    // 当页面加载完成,就会执行mounted函数,自动执行函数
	    mounted() { // 自动执行
            // console.log('mounted')
		    setInterval(() => {
                this.content += 1
		    }, 1000)
	    },
        template: '<div>{{content}}</div>' // 渲染内容
    }).mount('#root'); // 渲染节点
</script>
</html>

此后的代码省略了html的head、body部分,只写js部分代码

1-3 编写字符串反转和内容隐藏小功能

	// 反转字符串
    // Vue.createApp({
    //     data() {
    //         return {
    //             content: 'hello world'
    //         }
    //     },
    //     methods: { // 定义函数
    //         handleBtnClick() {
    //             // 将content内容反转
    //             this.content = this.content.split('').reverse().join('')
    //         }
    //     },
    //     template: `
    //       <div>
    //       	{{ content }}
    //       <!-- 给button元素绑定指令v-on:click,v-on:绑定事件,click:绑定的是点击事件,
    //       		事件会执行一个函数,handleBtnClick表示执行的函数名
    //       -->
    //       	<button v-on:click="handleBtnClick">反转</button>
    //       </div>
    // 	`
    // }).mount('#root');

    // 显示/隐藏
    Vue.createApp({
        data() {
            return {show: true}
        },
        methods: {
            handleBtnClick() {
                // 显示/隐藏
                this.show = !this.show
            }
        },
        template: `
          <div>
          <!-- v-if:标签的存在与否,此处取决于show的值-->
          <span v-if="show">hello world</span>
          <button v-on:click="handleBtnClick">显示/隐藏</button>
          </div>
		`
    }).mount('#root');

1-4 编写TodoList 小功能,了解循环和双向绑定

	// 循环list数据
    // Vue.createApp({
    //     data() {
    //         return {
    //             list: ['hello', 'world', 'dell', 'lee']
    //         }
    //     },
    //     template: `
    //       <ul>
    //       	<!-- 循环list数据-->
    //       	<li v-for="(item, index) of list">{{item}} {{index}}</li>
    //       </ul>
    // 	`
    // }).mount('#root');

    // 增加
    Vue.createApp({
        data() {
            return {
                inputValue: '',
                list: []
            }
        },
        methods: {
            handleAddItem() {
                this.list.push(this.inputValue)
                this.inputValue = '' // 增加完之后,input框清空
            }
        },
        template: `
          <div>
          <!-- v-model:将数据与输入框进行双向绑定-->
          <input v-model="inputValue"/>
          <button v-on:click="handleAddItem">增加</button>
          <ul>
            <li v-for="item of list">{{ item }}</li>
          </ul>
          </div>
		`
    }).mount('#root');

1-5 组件概念初探,对 TodoList 进行组件代码拆分

const app = Vue.createApp({ // 创建一个vue实例app
        data() {
            return {
                inputValue: '',
                list: []
            }
        },
        methods: {
            handleAddItem() {
                this.list.push(this.inputValue)
                this.inputValue = ''
            }
        },
        template: `
          <div>
          <!-- v-model:将数据与输入框进行双向绑定-->
          <input v-model="inputValue"/>
          <button
              v-on:click="handleAddItem"
              v-bind:title="inputValue"
          >
            <!-- v-bind:将标签上的数据与某个属性绑定-->
            <!-- 在标签内部表示数据,直接用{{}}-->
            增加 <!--  {{ inputValue }}-->
          </button>
          <ul>
            <todo-item
                v-for="(item, index) of list"
                v-bind:content="item"
                v-bind:index="index"
            />
          </ul>
          </div>
		`
    })

    // 在app上注册组件:组件是页面的一部分
    app.component('todo-item', {
        props: ['content', 'index'], // 接收外部传递的内容
        template: '<li>{{index}} -- {{content}}</li>'
    })

    // 在app上挂载实例
    app.mount('#root')

2 Vue 基础语法

2-1 Vue 中应用和组件的基础概念

	// createApp:创建一个Vue应用,存储到app变量当中
    // 传入的参数:这个应用最外层的组件该如何展示
    // mvvm设计模式: m->model 数据,v->view 视图,vm->viewModel 视图数据连接层
    const app = Vue.createApp({
        // 以下内容是app的参数
        // model
        data() {
            return {
                message: 'hello world'
            }
        },
        // view
        template: "<div>{{message}}</div>"
    })
    // vm:Vue的根组件,viewModel
    const vm = app.mount('#root')
    // 可以通过vm操作数据
    // vm.$data.message = 'bye'

2-2 理解 Vue 中的生命周期函数

/**
     * 生命周期函数:在某一时刻会自动执行的函数  (钩子=生命周期函数)
     * 关键点:某一时刻
     *       自动执行:eg.mounted()
     */
    const app = Vue.createApp({
        data() {
            return {
                message: 'hello world'
            }
        },
        // 在实例生成之前自动执行的函数
        beforeCreate() {
            console.log('beforeCreate')
        },
        // 在实例生成之后自动执行的函数
        created() {
            console.log('created')
        },
        // 在组件内容被渲染到页面之前自动执行的函数
        beforeMount() {
            console.log(document.getElementById('root').innerHTML, 'beforeMount') //  beforeMount
        },
        // 在组件内容被渲染到页面之后自动执行的函数
        mounted() {
            console.log(document.getElementById('root').innerHTML, 'mounted') // <div>hello world</div> mounted
        },
        // 当数据发生变化时,会自动执行的函数
        beforeUpdate() {
            console.log(document.getElementById('root').innerHTML, 'beforeUpdate') // <div>hello world</div> beforeUpdate
        },
        // 当数据发生变化,页面重新渲染后,会自动执行的函数
        updated() {
            console.log(document.getElementById('root').innerHTML, 'updated') // <div>bye</div> beforeUpdate
        },
	    // 当vue应用场景失效时,会自动执行的函数
        beforeUnmount() {
            console.log(document.getElementById('root').innerHTML, 'beforeUnmount') // <div>hello world</div> beforeUnmount
        },
        // 当vue应用场景失效时,且 dom 完全销毁后,会自动执行的函数
        unmounted() {
            console.log(document.getElementById('root').innerHTML, 'unmounted') // unmounted
        },
	    // 如果没有template属性,内容写在了body下的div root下,生命周期函数也是如此
        template: "<div>{{message}}</div>" // 先将模板变成函数,再渲染
    })
    const vm = app.mount('#root')

2-3 常用模版语法讲解

    const app = Vue.createApp({
        data() {
            return {
                message: 'hello world',
                disabled: false,
                show: true,
                event: 'click' // 动态参数
            }
        },
        methods: {
            handleClick() {
                alert('click')
            },
            // pd(e) {
            //     e.preventDefault() // 阻止默认行为
            // }
        },
        template: `
          <!--
              {{message}}:花括号内部可写js表达式
              v-once:表达式只执行一次,可以提高渲染性能
          -->
          <div v-once>{{ message }}</div>
          <!--
                v-html="message":在div上通过html方式展示message变量
                v-bind:title="message":展示标签内容,并与数据message进行绑定
                v-bind:title等价于:title(简写)
         -->
          <div v-bind:title="message">hello world</div>
          <!-- 与disabled值进行绑定,设置输入框input的状态 -->
          <input v-bind:disabled="disabled"/>
          <!-- v-if="show":div是否展示有show决定 -->
          <div v-if="show">{{ message }}</div>
          <!--
              1.v-on:事件绑定,事件方法handleClick写在methods中
              2.v-on:等价于@(简写)
              3.:[event]:动态属性
              v-on:click="handleClick"
              @click="handleClick"
              @[event]="handleClick"
          -->
          <div @click="handleClick">{{ message }}</div>
          <!--
            	阻止默认行为:
          		1.在函数内部完成功能:e.preventDefault()
				2.使用修饰符:.prevent(常用)
          -->
		<!-- <form action="https://www.baidu.com" @click="pd">-->
          <form action="https://www.baidu.com" @click.prevent="handleClick">
           <button>提交</button>
          </form>
      `
    })
    app.mount('#root')

2-4 数据,方法,计算属性和侦听器

    /**
     * data methods computed watcher
     * computed和methods实现相同的功能时,选computed,因为有缓存
     * computed和watcher实现相同的功能时,选computed,因为更简洁
     */
    const app = Vue.createApp({
        data() {
            return {
                message: 'hello world',
                count: 2,
                price: 5,
                newTotal: 10,
            }
        },
        methods: {
            // 方法中this指向vue实例
            // 不要使用箭头函数,因为箭头函数的this指向其外层的this
            handleClick() {
                console.log('click', this)
            },
            formatString(string) {
                return string.toUpperCase()
            },
            // 只要页面重新渲染,才会重新计算
            getTotal() {
                return Date.now()
                // return this.count * this.price
            }
        }, computed: {
            total() {
                // 当计算属性依赖的内容发生变更时,才会重新执行计算
                return Date.now()
                // return this.count * this.price
            }
        },
        // 当遇到异步情况的时候,可以使用watch
        // watch可以监听变量的改变,进行异步操作
        watch: {
            // price发生变化时函数会执行
            // price() {
            //     setTimeout(() => {
            //         console.log('price change')
            //     }, 1000)
            // },

            // 也可以实现计算属性的功能,实际上watch就是计算属性的底层实现
            price(cur, pre) {
                console.log(cur, pre)
                this.newTotal = cur * this.count
            }
        },
        template: `
          <!-- methods中的函数既可以在绑定事件的时候使用,也可以在js表达式{{}}中使用-->
          <div @click="handleClick">{{ formatString(message) }}</div>
          <!-- computed:计算属性
          	   当计算属性computed和方法methods实现的效果相同时,选computed,计算属性内部有缓存机制,会更高效
          -->
          <div>{{ message }} total:{{ total }}   getTotal():{{ getTotal() }}</div>
          <!-- 计算属性computed和监听watch实现的效果相同时,选computed -->
          <div>newTotal:{{ newTotal }}</div>
		`
    })
    const vm = app.mount('#root')

2-5 样式绑定语法

   const app = Vue.createApp({
       data() {
           return {
               classString: 'red',
               classObject: {red: true, green: true}, // 变量名:展示的样式值,变量值:是否展示
               classArray: ['red', 'green', {brown: true}],
            styleString: 'color: yellow', // 不建议使用
            styleObject: { // 建议使用对象的形式来写
                   color: 'orange',
               background: 'skyblue'
            }
           }
       },
       template: `
         <!-- 1.通过字符串方式,改变样式颜色 -->
         <div v-bind:class="classString">hello world</div>
         <!-- 对象 -->
         <div v-bind:class="classObject">hello world</div>
         <!-- 数组-->
         <div :class="classArray">hello world</div>

         <!-- 2.div父组件,demo子组件 -->
         <div :class="classString">
          hello world
          <!--
           demo组件上有两个标签,直接在demo上设置属性不能用
           解决办法:1)分别给one two写class属性
           2)给需要设置属性的标签上写:class="$attrs.class",使其去获取父组件的属性
          -->
          <demo class="green"/>
         </div>
         <!-- 3.行内样式 -->
         <!-- 1)直接在行内样式写 -->
         <div style="color: yellow">hello world</div>
         <!-- 2)与styleString数据绑定 (不建议使用)-->
         <div :style="styleString">hello world</div>
         <!-- 3)使用对象的形式表示 (建议使用)-->
         <div :style="styleObject">hello world</div>
   `
   })

   app.component('demo', {
       template: `
      <div :class="$attrs.class">one</div>
      <div>two</div>
`
   })
   const vm = app.mount('#root')

2-6 条件渲染

const app = Vue.createApp({
    data() {
        return {
            show: true,
         conditionOne: false,
         conditionTwo: true,
        }
    },
 template: `
   <!-- v-if & v-show -->
   <!-- v-if通过控制dom来展示元素,如果show=false,直接删除该div -->
  <div v-if="show">hello world</div>
   <!-- v-show通过控制style属性来展示元素,如果show=false,style="display: none" -->
  <div v-show="show">hello world</div>
   <!-- 总结:如果需要频繁改变dom元素显示与否,选v-show -->

   <!--if / else if /else-->
      <div v-if="conditionOne">if</div>
      <div v-else-if="conditionTwo">else</div>
      <div v-else>else</div>
 `
})
const vm = app.mount('#root')

2-7 列表循环渲染

    const app = Vue.createApp({
        data() {
            return {
                listArray: ['hello', 'world'],
                listObject: {
                    name: 'mys',
                    job: 'student'
                },
            }
        },
        methods: {
            handleAddBtnClick() {
                // 1.使用数组的变更函数 push pop shift unshift splice sort reverse
                // this.listArray.push('hello')
                // 2. 直接替换数组
                // this.listArray = ['bye', 'world'].filter(item => item === 'bye')
                // 3.直接更新数组的内容
                // this.listArray[1] = 'hhhh'

                // 直接添加对象的内容,也可以自动的展示出来
                this.listObject.age = '18'
                this.listObject.sex = 'female'
            }
        },
        template: `
          <div>
          <!-- 改变值 -->
          <!-- :key:表示是否可以复用,最好是唯一值 -->
          <div v-for="(item, index) in listArray" :key="index">
            {{ item }} -- {{ index }}
          </div>
          <button @click="handleAddBtnClick">新增1</button>

          <!--
          		for循环的优先级高于if,同时写if不生效
          		可以用一个占位符template写v-for
          		再用一个div写v-if
          -->
          <div>
            <!-- template占位符,不会在页面上做任何渲染 -->
            <template
                v-for="(value, key, index) in listObject"
                :key="index"
            >
              <div v-if="key !== 'name'">
                {{ key }} -- {{ value }} -- {{ index }}
              </div>
            </template>
          </div>
          <button @click="handleAddBtnClick">新增2</button>

          <div v-for="item in 5">{{ item }}</div>
          <button @click="handleAddBtnClick">新增3</button>
          </div>
		`
    })
    const vm = app.mount('#root')

2-8 事件绑定

      template: `
        <div>
         {{ counter }}
         <!-- 1.event原生事件,如果函数需要传递其他参数和原生event,可以使用$event传递原生事件 -->
         <!-- <button @click="handleBtnClick(2, $event)">button</button>-->
         <!-- 2.如果事件绑定多个函数,函数间可以用,隔开,注意需要加() -->
         <!-- <button @click="handleBtnClick1(), handleBtnClick2()">button</button>-->
         <!--
         3.事件修饰符:
         .stop     停止冒泡
         .self     自身标签出发才有效,子标签触发无效
         .prevent   阻止默认行为
         .capture   把事件的运营模式变成捕获模式
         .once     事件的绑定只执行一次
         .passive
     -->
         <div @click.self="handleDivClick">
           div自身
           <button @click.stop="handleBtnClick">button</button>
         </div>
         <!--
               4.按键修饰符:
                   .enter 按下enter才执行函数
                   .tab/.delete/.esc/.up ......
          -->
         <input @keydown.enter="handleKeyDown"/>
         <!--
               5.鼠标修饰符:
                   .left/.right/.middle
          -->
         <div @click.left="handleClick">鼠标修饰符</div>
         <!--
               6.精确饰符:
                   .exact  只有精确按下指定的键才生效
          -->
         <div @click.ctrl.exact="handleClick">精确饰符</div>
        </div>
`
  })

2-9 表单中双向绑定指令的使用

    const app = Vue.createApp({
        data() {
            return {
                message: 'hello',
                checkbox: [],
                radio: '',
                select: '',
                options: [{
                    text: 'A', value: 'A',
                }, {
                    text: 'B', value: 'B',
                }, {
                    text: 'C', value: {value: 'C'},
                }],
                checkboxValue: 'hello',
	            lazy: 'lazy',
	            number: 12,
            }
        },
        template: `
          <div>
	          <!--
	                1.v-model:双向绑定  输入框与变量message绑定
	                input/textarea/checkbox/radio/select
	          -->
	          {{ message }}
	          <input v-model="message"/>
	          <textarea v-model="message"/>
	          {{ checkbox }}
	          <!-- checkbox:可以写多个,可以用一个数组来保存选中的value -->
	          aa <input type="checkbox" v-model="checkbox" value="aa"/>
	          bb <input type="checkbox" v-model="checkbox" value="bb"/>
	          {{ radio }}
	          cc <input type="radio" v-model="radio" value="cc"/>
	          dd <input type="radio" v-model="radio" value="dd"/>
	          {{ select }}
	          <select v-model="select" multiple>
	            <!-- 不加multiple可以写下面的提示 -->
	            <!-- <option disabled value="">请选择内容</option>-->
	            <!-- options中的value也可以是一个对象 -->
	            <option v-for="item in options" :value="item.value">{{ item.text }}</option>
	          </select>
	          {{ checkboxValue }}
	          <!-- 使用true-value/false-value可以改变checkbox的值 -->
	          <input type="checkbox" v-model="checkboxValue" true-value="hello" false-value="world"/>
	
	          <!--
	                2.修饰符
	                    .lazy 		当input框失去焦点时才生效
	                    .number 	将输入框的类型变为number
	                    .trim 		去除字符串前后空格
	           -->
	          {{ lazy }}
	          <input v-model.lazy="lazy"/>
	          {{ typeof number }}
	          <input v-model.number="number"/>
          </div>
		`
    })
    const vm = app.mount('#root')

3 探索组件的理念

3-1 组件的定义及复用性,局部组件和全局组件

在这里插入图片描述

 /**
  * 定义一个局部组件,在app中不能直接使用,
  * 需要写上:components: {'mys': counter_part}引入局部组件
  * 定义局部组件,通常将首字符大写
  */
 const CounterPart = {
     data() {
         return {
             count: 1
         }
     },
     template: `<div @click="count += 1">{{count}}</div>`
 }

 const HelloWorld = {
     template: `<div>hello world</div>`
 }

 const app = Vue.createApp({
     /**
      * 创建一个vue实例,渲染根组件
      * 组件:对页面的展示
      * 组件可以被复用,且其数据相互独立
      * 组件在app中定义了,即使没使用,也会挂在app上
      * 在app中定义的组件是全局组件,在处处都能使用,性能不高,使用简单,名字通常用小写字母,中间用-隔开
      * 局部组件需要定义并注册后才能使用,性能高,但是使用麻烦,名字通常大写字母开头,驼峰式命名
      * 局部组件使用时,需要名字和组件间映射对象,如果不写映射,按照上面的命名方式,vue底层会自动映射
      */

     // 在vue实例中使用components引入局部组件
     components: {
         // 'CounterPart': CounterPart,
         // CounterPart 命名和组件名相同可以简写
      // 'hello-world': HelloWorld

      // vue会自动进行映射,将驼峰命名格式映射为:counter-part短横线分割的格式
      CounterPart, HelloWorld
     },
     template: `
<div>
   <!-- 全局组件 -->
   <hello/>
   <world/>
   <counter-parent/>
   <counter/>
   <counter/>

   <!-- 局部组件 -->
   <counter-part/>
   <hello-world/>
</div>
`
 })

 // 定义子组件 (全局组件)
 app.component('hello', {
     template: `<div>hello</div>`
 })
 app.component('world', {
     template: `<div>world</div>`
 })
 app.component('counter-parent', {
     template: `<counter/>`
 })
 app.component('counter', {
     data() {
         return {
             count: 1
         }
     },
     template: `<div @click="count += 1">{{count}}</div>`
 })
 const vm = app.mount('#root')

3-2 组件间传值及传值校验

  const app = Vue.createApp({
      data() {
          return {
              num: 123,
              // num: () => alert('num')
          }
      },
      template: `
        <div>
        <!--
       1.父组件调用子组件,传递内容给子组件使用
       2.静态参数 & 动态参数
       静态传参:不带:,直接使用,一般只能传递字符串
       动态传参:带:,比如 :content,由data里面的数据决定
  -->
           <test :content="num"/>
        </div>
`
  })
  /**
   * 3.父组件->子组件:接收数据、类型校验
   * type:String,Boolean,Array,Object,Function,Symbol
   * required: 必选
   * default:默认值
   */
  // 创建一个全局组件
  app.component('test', {
      // props: ['content'], 直接接收父组件传递来的数据
      // 如果需要对父组件传递来的数据做验证
      props: {
          // 如果传递来的数据不是String类型的,控制台会出现警告提示:type check failed for prop "content"
          // content: String,
          // content:Function,

       content: {
            type: Number,
        // default: 123, // 默认值
        // required: true, // 如果没写default,不传参会有警告

        // 深度校验
              validator: function (value) { // value父组件传递来的值
                  return value < 1000
              },
        // default也可以是一个函数
              default: function () {
                  return 456
              }
       },
      },
      methods: {
          handleClick() {
              alert('handleClick')
           this.content() // 调用父组件传递来的函数
          }
      },
      template: `
        <div @click="this.handleClick">{{ content }}</div>`
  })

  const vm = app.mount('#root')

3-3 单向数据流的理解

  const app = Vue.createApp({
      data() {
          return {
              params: {
                  content: 123,
                  a: 1,
                  b: 2,
              },
              dataAbc: 123,
              num: 1,
          }
      },
      template: `
        <div>
        <!-- <test :content="num" :a="a" :b="b"/> -->
        <!-- 1.如果参数太多,可以把参数封装到一个对象params中 -->
        <!--  <test v-bind="params"/>等价于:
              <test :content="params.content" :a="params.a" :b="params.b"/>
        -->
        <test v-bind="params"/>

        <!-- 2.如果参数名太长,用-分隔,不能使用驼峰命名,但是注意:子组件接收参数时需要用驼峰命名
              即:属性传时使用:data-abc  接收时:dataAbc
        -->
        <test data-abc="123"/>

        <counter :count="num"/>
        </div>
`
  })
  app.component('test', {
      props: ['content', 'a', 'b', 'dataAbc'],
      template: `
        <div>{{ content }} -- {{ a }} -- {{ b }} -- {{ dataAbc }}</div>
`
  })

  /**
   * 3.单向数据流
   * 父组件可以向子组件传递数据,但数据是只读的,子组件不能修改
   * 避免组件之间的数据耦合
   */
  app.component('counter', {
      props: ['count'],
   data() {
          return {
              myCount: this.count
          }
   },
      template: `
        <!-- <div @click="count += 1">{{ count }}</div> 不能修改父组件传递过来的数据-->
        <div @click="myCount += 1">{{ myCount }}</div>
      `
  })
  const vm = app.mount('#roo

3-4 Non-Props 属性是什么

 /**
  * Non-props属性:
  * 父组件给子组件传递数据时,子组件不通过props接收,
  * 此时底层会将父组件传递过来的属性放在最外层div属性上,
  * 如果在子组件上写:inheritAttrs: false,就不会放在最外层div上
  * 使用:通常用于 style class属性
  */
 const app = Vue.createApp({
  template: `
   <div>
       <counter msg="hello" msg2="world"/>
</div>
  `
 })
 app.component('counter', {
     // inheritAttrs: false,
  mounted() {
         console.log(this.$attrs.msg) // hello
  },
     template: `
   <!-- 如果有多个div,Non-prop属性就不会生效,需要使用v-bind="$attrs"决定是否接收属性
      v-bind="$attrs":    接收所有属性
      :msg="$attrs.msg":  接收指定属性
   -->
   <div v-bind="$attrs">Counter</div>
   <div :msg="$attrs.msg">Counter</div>
   <div>Counter</div>
`,
 })
 const vm = app.mount('#root')

3-5 父子组件间如何通过事件进行通信

 /**
  * 父子组件通信的流程:
  * 1.子组件接收父组件传递来的count
  * 2.点击时触发了事件:handleClick,该事件向父组件传递:add事件,也能传递参数
  * 3.父组件监听到add事件,通过handleAdd进行处理,修改count
  * 4.最后修改了的count传递给了子组件
  */
 const app = Vue.createApp({
  data() {
       return{
           count: 1
       }
  },
  methods: {
         handleAdd(param) {
             this.count += param
         }
  },
     template: `
   <div>
       <!-- 注意:触发事件时:驼峰命名,监听时间:-分隔命名 -->
       <counter :count="count" @add="handleAdd"/>
</div>
  `
 })
 app.component('counter', {
     props: ['count'],
  // emits:表示触发了哪些事件
  emits: ['add'],
  // embeds: {
     //     // 可以对参数进行校验
     //     add: (count) => {
     //         if (count > 0) {
     //             return true
     //         }
     //         return false
     //     }
  // },
  methods: {
         handleClick() {
             this.$emit('add', 2)
          // 事件处理的逻辑也可以直接在子组件内完成,再传递给父组件
             // this.$emit('add', this.count + 3)
         }
  },
     template: `
       <div @click="handleClick">{{ count }}</div>
     `,
 })
 const vm = app.mount('#root')

使用v-model简化

  /**
   * 使用v-model简化
   * 1.父组件中定义一个数据count,通过v-model传递给子组件
   * 2.子组件使用modelValue接收参数,注意:接收参数名只能是:modelValue
   * 3.点击触发事件handleClick,该事件向父组件传递的事件名只能是:update:modelValue
   *  传递的参数modelValue会替换父组件的count
   */
  const app = Vue.createApp({
      data() {
          return {
              count: 1
          }
      },
      template: `
        <counter v-model="count"/>
        <!-- 如果想要指定modelValue的名字,可以使用:app,下面全部改成app即可 -->
        <!-- <counter v-model:app="count"/> -->
`
  })
  app.component('counter', {
      props: ['modelValue'],
      methods: {
          handleClick() {
              this.$emit('update:modelValue', this.modelValue + 3)
          }
      },
      template: `
        <div @click="handleClick">{{ modelValue }}</div>
`,
  })
  const vm = app.mount('#root')

3-6 组件间双向绑定高级内容

**1、父子组件间的通信 v-model 传递多个参数 **

    /**
     * 父子组件间的通信 v-model
     *  传递多个参数
     */
    const app = Vue.createApp({
        data() {
            return {
                count: 1,
	            count1: 1,
            }
        },
        template: `
          <!-- 如果需要传递2个参数,可以写2个v-model,并通过:count的方式指定名字 -->
          <counter v-model:count="count" v-model:count1="count1"/>
		`
    })
    app.component('counter', {
        props: ['count', 'count1'],
        methods: {
            handleClick() {
                this.$emit('update:count', this.count + 3)
            },
            handleClick1() {
                this.$emit('update:count1', this.count1 + 3)
            }
        },
        template: `
          <div @click="handleClick">{{ count }}</div>
          <div @click="handleClick1">{{ count1 }}</div>
		`,
    })
    const vm = app.mount('#root')

2、修饰符的使用

  /**
   * 父子组件间的通信 v-model
   *  修饰符的使用:
   *      .uppercase
   *
   * 1.父组件中:v-model.uppercase
   * 2.父组件会传递一个modelModifiers参数接收修饰符,注意:modelModifiers名字是固定的
   * 3.在点击事件handleClick,获取修饰符的值,并调用相应的函数进行处理
   * 4.向父组件传递事件:update:modelValue和新的参数newValue
   * 5.父组件获取到事件后,对count变量的值进行修改
   */
  const app = Vue.createApp({
      data() {
          return {
              count: 'a',
          }
      },
      template: `
        <counter v-model.uppercase="count"/>
`
  })
  app.component('counter', {
      props: {
          'modelValue': String,
          // 传递过来的修饰符
          'modelModifiers': {
              // 如果不传修饰符,默认空对象;如果传了修饰符会放到对象中
              default: () => ({})
          }
      },
      // mounted() {
      //     console.log(this.modelModifiers)
      // },
      methods: {
          handleClick() {
              let newValue = this.modelValue + 'b'
              if (this.modelModifiers.uppercase) {
                  newValue = newValue.toUpperCase()
              }
              this.$emit('update:modelValue', newValue)
          },
      },
      template: `
        <div @click="handleClick">{{ modelValue }}</div>
`,
  })
  const vm = app.mount('#root')

3-7 使用插槽和具名插槽解决组件内容传递问题

    /**
     * slot 插槽
     * slot不能绑定事件,可以在用一个span标签包裹slot,用于绑定事件
     * slot中可以传递:标签、字符串、子组件
     *
     * slot中使用的数据作用域问题:
     * 父模板里调用的数据属性,都用的是父模板里的数据
     * 子模板里调用的数据属性,都用的是子模板里的数据
     * 
     * slot的简写:v-slot:header => #header
     */
    const app = Vue.createApp({
        data() {
            return {
                text: '提交',
            }
        },
        template: `
          <!--
			  需求:第1个myform:提交组件是 div;第2个myform:提交组件是 button
					  将myform变成双标签,在标签内写入需要的东西
					  可以将需要的内容放入data中,再获取
		   -->
          <myform>
          <!-- slot -->
          	<div>{{ text }}</div>
          </myform>
          <myform>
          	<button>{{ text }}</button>
          </myform>
          <myform>
          </myform>

          <layout>
          	<!-- 具名插槽:
          		用占位符template指定不同的slot
          	-->
          	<template v-slot:header>
	          <div>header</div>
         	 </template>
            <!-- slot简写 -->
            <template #footer>
              <div>footer</div>
            </template>
          </layout>
		`
    })
    app.component('myform', {
        methods: {
            handleClick() {
                alert('click')
            }
        },
        template: `
          <div>
          <input/>
          <span @click="handleClick">
	            <!-- 可以设定默认值 -->
	          	<slot>default value</slot>
	          </span>
          </div>
		`,
    })

    app.component('layout', {
        template: `
            <div>
                <slot name="header"></slot>
                <div>content</div>
                <slot name="footer"></slot>
			</div>
        `
    })
    const vm = app.mount('#root')

3-8 作用域插槽

    /**
     * 作用域插槽
     * 1.父组件调用list的子组件,即传递给子组件的slot div
     * 2.子组件调用slot,通过div的形式循环显示内容
     * 3.调用slot时,将item数据通过slotProps数据对象传递给slot
     *
     * 当子组件渲染的内容由父组件决定时
     * 通过作用域插槽,能够让父组件调用子组件的数据
     */
    const app = Vue.createApp({
        template: `
<!--           <list v-slot="slotProps"> -->
<!--             <div>{{ slotProps.item }}</div> -->
<!--           </list> -->
         <!-- 可以使用解构的方式简化 -->
          <list v-slot="{item}">
            <div>{{ item }}</div>
          </list>
      `
    })

    app.component('list', {
        data() {
            return {list: [1, 2, 3]}
        },
        template: `
          <div>
           <slot v-for="item in list" :item="item"/>
          </div>
      `
    })
    const vm = app.mount('#root')

3-9 动态组件和异步组件

  /**
   * 需求:通过一个变量控制input-item、common-item的展示与隐藏
   *
   * 动态组件:根据数据的变化,结合component标签,来随时动态切换组件的显示
   * 异步组件:异步执行某些组件的逻辑
   */

  const app = Vue.createApp({
      data() {
          return {currentItem: 'input-item'}
      },
      methods: {
          handleClick() {
              this.currentItem = this.currentItem === 'input-item' ? 'common-item' : 'input-item'
          }
      },
      template: `
        <!--          <input-item v-show="currentItem === 'input-item'"/>-->
        <!--          <common-item v-show="currentItem === 'common-item'"/>-->
        <!-- keep-alive缓存,component动态组件 -->
       <keep-alive>
           <component :is="currentItem"/>
       </keep-alive>
       <button @click="handleClick">切换</button>

        <div>
         <sync-common-item /> <!-- 同步组件 -->
         <async-common-item /> <!-- 异步组件 -->
        </div>

		`
  })

  app.component('input-item', {
      template: `
        <input />
		`
  })
  app.component('common-item', {
      template: `
        <div>hello world</div>
`
  })

  // 同步
  app.component('sync-common-item', {
      template: `
        <div>hello world</div>
`
  })
  // 异步
  app.component('async-common-item', Vue.defineAsyncComponent(() => {
      return new Promise((resolve, reject) => {
          setTimeout(() => {
              resolve({
                  template: `<div>this is an async component</div>`
              })
          }, 4000)
      })
  }))
  const vm = app.mount('#root')

3-10 基础语法知识点查缺补漏

  /**
   * v-once:让某个元素标签只渲染一次
   * ref:获取dom节点 / 组件的一个用法 (直接操作dom是要慎重)
   * provide / inject 多层组件传值,使用provide提供数据,inject注入数据
   */
  const app = Vue.createApp({
      data() {
          return {count: 1}
      },
   // provide: {
      //     count: 2,
   // },
   // 如果是传递data中的数据,要使用函数形式
   // 此处传递的数据是一次性的,需要类似双向绑定的效果需要响应的知识,后面会讲
   provide() {
          return {
              count: this.count
          }
   },
      mounted() {
          // 获取dom最好在mounted中,因为此时页面已经加载完成。但是最好不要直接操作dom
          // console.log(this.$refs.count.innerHTML = 'hello')
          // console.log(this.$refs.common.syaHello())
      },
      template: `
        <div>
         <div ref="count">
           {{ count }}
         </div>
             <common-item ref="common"/>

         <!-- 层层传递数据count:父组件->child->child-child -->
             <child :count="count"/>
            <button @click="count += 1">Add</button>
        </div>

`
  })
  app.component('common-item', {
      methods: {
          syaHello() {
              alert('hello')
          }
      },
   template: `<div>hello world</div>`
  })
  app.component('child', {
      props: ['count'],
      // template: `<child-child :count="count"/>`
      template: `<child-child/>`
  })
  app.component('child-child', {
      // props: ['count'],
   inject: ['count'],
      template: `<div>{{ count }}</div>`
  })
  const vm = app.mount('#root')

4 Vue 中的动画

4-1 使用 Vue 实现基础的 CSS 过渡与动画效果

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>4-1</title>
   <script src="https://unpkg.com/vue@next"></script>
   <style>
        /* 动画 */
        .animation {
            animation: leftToRight 3s;
        }

        @keyframes leftToRight {
            0% {
                transform: translateX(-100px);
            }
            50% {
                transform: translateX(-50px);
            }
            0% {
                transform: translateX(0px);
            }
        }

        /* 过渡 */
        .transition {
            transition: 3s background-color ease;
        }

        .blue {
            background-color: blue;
        }

        .green {
            background-color: green;
        }

   </style>
</head>
<body>
<div id="root"></div>
</body>
<script>
    // 过渡 动画
    const app = Vue.createApp({
        data() {
            return {
                animate: {
                    animation: false
                },
                transit: {
                    transition: true,
                    blue: true,
                    green: false,
                },
                styleObj: {
                    background: 'red'
                }
            }
        },
        methods: {
            handleClick() {
                this.animate.animation = !this.animate.animation
            },
            handleClick1() {
                this.transit.transition = !this.transit.transition
                this.transit.blue = !this.transit.blue
                this.transit.green = !this.transit.green
            },
            handleClick2() {
                if (this.styleObj.background === 'red') {
                    this.styleObj.background = 'yellow'
                } else {
                    this.styleObj.background = 'red'
                }
            }
        },
        template: `
          <div>
             <!-- 动画 -->
             <div :class="animate">hello</div>
             <button @click="handleClick">切换</button>
             <!-- 过渡:css -->
             <div :class="transit">hello</div>
             <button @click="handleClick1">切换</button>
             <!-- 过渡:style样式 -->
             <div class="transition" :style="styleObj">hello</div>
             <button @click="handleClick2">切换</button>
          </div>
      `
    })
    const vm = app.mount('#root')
</script>
</html>

4-2 使用 transition 标签实现单元素组件的过渡和动画效果

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>4-2</title>
   <script src="https://unpkg.com/vue@next"></script>
   <!-- 引入动画样式库 -->
   <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"/>
   <style>
        /*入场动画:v-enter-from/to/active*/
        .v-enter-from {
            opacity: 0;
            color: red;
        }

        .v-enter-to {
            opacity: 1;
        }

        /*出场动画*/
        /*可以不写,因为出厂前opacity本身就是1*/
        /*.v-leave-from {*/
        /* opacity: 1;*/
        /*}*/
        .v-leave-to {
            opacity: 0;
        }

        /*在整个动画的过程中如何执行:入场、出场效果一样,可以写在一起*/
        .v-enter-active,
        .v-leave-active,
        .hello,
        .bye {
            animation: shake 3s;
            transition: all 3s ease-in;
        }

        /*动画*/
        @keyframes shake {
            0% {
                transform: translateX(-100px);
            }
            50% {
                transform: translateX(-50px);
            }
            100% {
                transform: translateX(0px);
            }
        }
   </style>
</head>
<body>
<div id="root"></div>
</body>
<script>
    /**
     * 单元素、单组件的入场、出场动画,可以给组件同时添加动画、过渡效果
     */
    const app = Vue.createApp({
        data() {
            return {
                show: true
            }
        },
        methods: {
            handleClick() {
                this.show = !this.show
            },
            // 在入场之前可以做的事
            handleBeforeEnter(el) {
                el.style.color = 'red'
            },
           // 执行时的操作
            handleEnterActive(el, done) {
                const animation = setInterval(() => {
                    const color = el.style.color
                    if (color === 'red') {
                        el.style.color = 'yellow'
                    } else {
                        el.style.color = 'red'
                    }
                }, 1000)
                // 5s后清除动画
                setTimeout(() => {
                    clearInterval(animation)
                   done() // 表明动画结束
                }, 5000)
            },
           // 执行完的操作
            handleEnterEnd() {
                alert(123)
            },
        },
        template: `
          <div>
             <!--
               1.可以在transition中重命名:<transition name="hello">
                  .v-enter-active => .hello-enter-active
                  固定写法:.v-enter-from  .v-enter-active  .v-enter-to
                        .v-leave-from  .v-leave-active  .v-leave-to
               2.也可以自定义名字:
                  <transition
                           enter-active-class="hello"
                           leave-active-class="bye"
                       >
                       在style中直接使用.hello / .bye即可
           -->
             <!--          <transition-->
             <!--              enter-active-class="hello"-->
             <!--              leave-active-class="bye"-->
             <!--          >-->

             <!-- 3.可以直接引入第三方animate.css的动画库: https://animate.style/-->
             <!--          <transition-->
             <!--              enter-active-class="animate__animated animate__bounce"-->
             <!--              leave-active-class="animate__animated animate__bounce"-->
             <!--          >-->

             <!--
                 4.type="transition":当同时有过渡和动画效果时,如果时间不一致,可以指定以其中的一个为准,控制同步执行
                 (1)type="transition" 以transition为准
                 (2):duration="1000":指定执行时间
                   :duration="{enter: 1000, leave:3000}" 也可以分别指定时间
            -->
             <!-- <transition :duration="1000"> -->

             <!--
                   5.如果想用js来控制动画效果,可以取消css控制  :css="false"
                       钩子:生命周期函数,在某个时刻被调用
                           @before-enter="handleBeforeEnter"   函数参数:el
                           @enter="handleEnterActive"             el done
                           @after-enter="handleEnterEnd"           el

                           @before-leave
                           @leave
                           @after-leave
             -->
             <transition
                 :css="false"
                 @before-enter="handleBeforeEnter"
                 @enter="handleEnterActive"
                 @after-enter="handleEnterEnd"
             >

               <div v-if="show">hello world</div>
               <!-- <div v-show="show">hello world</div>-->
             </transition>
             <button @click="handleClick">切换</button>
          </div>
      `
    })
    const vm = app.mount('#root')
</script>
</html>

4-3 组件和元素切换动画的实现

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>4-2</title>
  <script src="https://unpkg.com/vue@next"></script>
  <style>
    .v-enter-from {
      opacity: 0;
    }
    .v-enter-active,
    .v-leave-active {
      transition: opacity 3s ease-in;
    }
    .v-enter-to {
      opacity: 1;
    }

    .v-leave-from {
      opacity: 1;
    }
    .v-leave-to {
      opacity: 0;
    }

  </style>
</head>
<body>
<div id="root"></div>
</body>
<script>
  /**
   * 多个单元素标签之间的切换 transition
   *
   * 多个单组件标签之间的切换
   * 1.v-if v-else
   * 2.动态组件 <component :is="component" />
   */
  const ComponentA = {
    template: `<div>hello world</div>`
  }
  const ComponentB = {
    template: `<div>bye world</div>`
  }

  const app = Vue.createApp({
    data() {
      return {
        show: false,
        component: 'component-a',
      }
    },
    components: {
      'component-a': ComponentA,
      'component-b': ComponentB,
    },
    methods: {
      handleClick() {
        this.show = !this.show
      },
      handleClick1() {
        if (this.component === 'component-a') {
          this.component = 'component-b'
        } else {
          this.component = 'component-a'
        }
      },
    },
    template: `
          <div>
              <!-- mode="out-in":动画效果先隐藏再展示
                   appear:初次展示动画效果
              -->
             <transition mode="out-in" appear>
                <div v-if="show">hello world</div>
                <div v-else="show">bye world</div>
             </transition>
              <button @click="handleClick">切换</button>

              <transition mode="out-in" appear>
                <component-a v-if="show" />
                <component-b v-else="show" />
              </transition>
             <button @click="handleClick">切换</button>

              <transition mode="out-in" appear>
                <component :is="component" />
              </transition>
             <button @click="handleClick1">切换</button>
          </div>
      `
  })
  const vm = app.mount('#root')
</script>
</html>

4-4 列表动画

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>4-4</title>
   <script src="https://unpkg.com/vue@next"></script>
   <style>
       .list-item {
          display: inline-block;
          margin: 10px;
       }

       .v-enter-from,
       .v-leave-to {
          opacity: 0;
          transform: translateY(30px);
       }
       .v-enter-active,
       .v-leave-active {
          transition: all 1s ease-in;
       }
       .v-enter-to,
       .v-leave-from {
          opacity: 1;
           transform: translateY(0px);
       }

       /*其他列表项的移动效果*/
       .v-move {
          transition: all 1s ease-in;
       }
   </style>
</head>
<body>
<div id="root"></div>
</body>
<script>
  /**
   * 列表动画
   * transition-group
   * .v-move
   */

  const app = Vue.createApp({
    data() {
      return {
        list: [1, 2, 3],
      }
    },
    methods: {
      handleClick() {
        this.list.unshift(this.list.length + 1)
      },
    },
    template: `
      <div>
      <transition-group>
         <span class="list-item" v-for="item in list" :key="item">{{ item }}</span>
         <button @click="handleClick">增加</button>
      </transition-group>
      </div>
   `
  })
  const vm = app.mount('#root')
</script>
</html>

4-5 状态动画

 /**
  * 状态动画 svg
  */

 const app = Vue.createApp({
   data() {
     return {
       number: 1,
       animateNumber: 1,
     }
   },
   methods: {
     handleClick() {
       this.number = 10
       if (this.animateNumber < this.number) {
         const animation = setInterval(() => {
           this.animateNumber += 1
           if (this.animateNumber === 10) {
             clearInterval(animation)
           }
         }, 100)
       }
     },
   },
   template: `
     <div>
      <div>{{ animateNumber }}</div>
      <button @click="handleClick">增加</button>
     </div>
`
 })
 const vm = app.mount('#root')

5 Vue 中的高级语法

5-1 Mixin 混入的基础语法

 /**
  * mixin 混入 (vue3引入后不推荐)
  * 1.组件data,methods 优先级高于mixin data,methods优先级
  * 2.生命周期函数先执行mixin中的,再执行组件中的
  * 3.自定义的属性,组件的属性优先级高于mixin属性的优先级
  * 4.改变mixin优先级
  *
  * 局部 mixin
  * 全局 mixin(不推荐)
  */
   // 局部mixin
 const myMixin = {
     // data() {
     //   return {
     //     number: 2,
     //   }
     // },
     number: 2,
     created() {
       console.log('mixin created')
     },
     methods: {
       handleClick() {
         console.log('mixin handleClick')
       }
     },
   }

 const app = Vue.createApp({
   // data() {
   //   return {
   //     number: 1,
   //   }
   // },
   number: 1,
   mixins: [myMixin], // 使用mixin
   created() {
     console.log('created')
   },
   methods: {
     // handleClick() {
     //   console.log('handleClick')
     // }
   },
   template: `
     <div>
     <!--      <div>{{ number }}</div>-->
     <!--      <button @click="handleClick">点击</button>-->

     <!-- vue中所有没有被处理的内容都会被挂在options中 -->
     <div>{{ this.$options.number }}</div>
     <child/>
     </div>
`
 })

 // 修改mixin属性的优先级
 app.config.optionMergeStrategies.number = (mixinVal, appValue) => {
   return mixinVal || appValue
 }

 // 全局mixin
 // app.mixin({
 //   data() {
 //     return {
 //       number: 2,
 //     }
 //   },
 //   created() {
 //     console.log('mixin created')
 //   },
 //   methods: {
 //     handleClick() {
 //       console.log('mixin handleClick')
 //     }
 //   },
 // })

 app.component('child', {
   template: "<div>child</div>"
 })
 const vm = app.mount('#root')

5-2 开发实现 Vue 中的自定义指令

  /**
   * 自定义组件 directive
   * 1.局部
   *    (1) 定义指令
   *    (2) 在app中使用指令
   * 2.全局
   *    app.directive
   */

    // 局部自定义指令
    // const directives = {
    //     focus: {
    //       mounted(el) {
    //         el.focus()
    //       }
    //     }
    //   }

  const app = Vue.createApp({
      data() {
        return {show: true}
      },
      // directives,
      template: `
        <div>
        <div v-if="show">
          <input v-focus/>
        </div>
        </div>
	  `
    })

  // 全局自定义指令
  app.directive('focus', {
    // 执行前
    beforeMount() {
      console.log('beforeMount')
    },
    // 执行
    mounted(el) {
      el.focus()
    },
    // 更新前
    beforeUpdate() {
      console.log('beforeUpdate')
    },
    // 更新
    updated() {
      console.log('updated')
    },
    // 失效前
    beforeUnmount() {
      console.log('beforeUnmount')
    },
    // 失效
    unmounted() {
      console.log('unmounted')
    }
  })
  const vm = app.mount('#root')

小例子

/**
 * 小例子:控制输入框的位置
 */
const app = Vue.createApp({
    data() {
      return {distance: 100}
    },
    template: `
      <div>
      <!-- 参数binding.arg=left  参数的值:binding.value=distance -->
      <div v-pos:left="distance" class="header">
        <input/>
      </div>
      </div>
 `
  })

// app.directive('pos', {
//   mounted(el, binding) {
//     el.style.top = binding.value + 'px'
//   },
// })
// 如果只有一个函数,可以简写
app.directive('pos', (el, binding) => {
  el.style[binding.arg] = (binding.value + 'px')
})
const vm = app.mount('#root')
.header {
   position: absolute;
}

5-3 Teleport 传送门功能

 /**
  * teleport:传送门
  */
 const app = Vue.createApp({
   data() {
     return {
       show: false
     }
   },
   methods: {
     handleBtnClick() {
       this.show = !this.show
     },
   },
   template: `
     <div class="area">
     <button @click="handleBtnClick">按钮</button>
     <!-- 传送门:使得mask挂载到body下 -->
     <teleport to="body">
       <div class="mask" v-show="show"></div>
     </teleport>
     </div>
`
 })
 const vm = app.mount('#root')
.area {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 200px;
    height: 200px;
    background-color: skyblue;
}

/*蒙层*/
.mask {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    background-color: black;
    opacity: 0.5;
}

5-4 更加底层的 render 函数

 /**
  * render函数
  * template底层编译会生成render函数
  * template -> render -> h -> 虚拟DOM(JS对象) -> 真是DOM -> 展示到页面上
  */
 const app = Vue.createApp({
   template: `
     <my-title :level="2">
       hello mys
  </my-title>
`
 })
 app.component('my-title', {
   props: ['level'],
   render() {
     const { h } = Vue
     /**
      * 返回:虚拟DOM 1.让vue的性能更快 2.使得vue可以跨平台使用
      * 虚拟DOM eg.
      * <div>hello</div>
      * {
      *     tagName: 'div,  标签名
      *     text: hello,    标签属性
      *     attributes: {}  节点内容
      * }
      * 第三个参数也可以是一个数组,里面可以继续嵌套
      */
     // this.$slots.default():获取slot中的值 注意:slots要加上s
     // return h('h' + this.level, {}, this.$slots.default())
     return h('h' + this.level, {}, [this.$slots.default(),
     h('h4', {}, 'mys')])
   }
   // template: `
   //   <h1 v-if="level === 1"><slot /></h1>
   //   <h2 v-if="level === 2"><slot /></h2>
// `
 })
 const vm = app.mount('#root')

5-5 插件的定义和使用

 /**
  * plugin插件:把通用性的功能封装起来
  */
 const myPlugin = {
   // app:使用插件的vue实例 options:传递的对象
   // 实例拿到之后,可以做很多扩展
   install(app, options) {
     // console.log(app, options)
     app.provide('name', 'mys')
     app.directive('focus', {
       mounted(el) {
         el.focus()
       }
     })
     app.mixin({
       mounted() {
         // 会输出2次,根组件、子组件都会打印
         console.log('mixin')
       }
     })
     // 底层扩展全局属性
     app.config.globalProperties.$sayHello = 'hello world'
   }
 }
 const app = Vue.createApp({
   template: `
     <my-title />
`
 })
 app.component('my-title', {
   inject: ['name'], // 声明provide传递的参数
   mounted() {
     console.log(this.$sayHello)
   },
   template: `<div>{{name}}<input v-focus /></div>`
 })
 app.use(myPlugin, {name: 'mys'})
 const vm = app.mount('#root')

5-6 数据校验插件开发实例

 /**
  * 读数据做校验的插件
  */
 const app = Vue.createApp({
   data() {
     return {
       name: 'mys',
       age: 18
     }
   },
   // 校验
   rules: {
     age: {
       validate: age => age > 10,
       message: 'too young'
     },
     name: {
       validate: name => name.length >= 3,
       message: 'too short'
     }
   },
   template: `
     <div>name:{{ name }},age:{{ age }}</div>
`
 })

 const validatePlugin = (app, options) => {
   app.mixin({
     created() {
       for (let key in this.$options.rules) {
         const item = this.$options.rules[key]
         // this: app实例
         // 监听实例数据变化
         this.$watch(key, (value) => { // 注意:此处要传入value参数,表示修改后的值
           const result = item.validate(value)
           if (!result) console.log(item.message)
         })
       }
     }
   })
 }
 app.use(validatePlugin)

 const vm = app.mount('#root')

6 Composition API

6-1 Setup 函数的使用

 /**
  * Composition API:维护代码更方便
  */
 const app = Vue.createApp({
   data() {
     return {
       name: 'mys',
     }
   },
   /**
    * setup在实例被完全初始化之前执行
    * setup中不能使用this,因为实例还没有被初始化
    * setup不能调用外部实例方法,而实例方法可以调用setup
    */
   setup(props, context) {
     return {
       name: 'mys',
       handleClick: () => {
         console.log('handleClick')
       }
     }
   },
   template: `
     <div @click="handleClick">{{ name }}</div>
`
 })
 const vm = app.mount('#root')

6-2 ref,reactive 响应式引用的用法和原理

 /**
  * 响应式的引用
  * 原理:通过proxy对数据进行封装,当数据变化时触发模板等内容的更新
  * ref:处理基础类型的数据
  * reactive:处理非基础类型的数据
  *
  * 可以替代data
  */
 const app = Vue.createApp({
   template: `
     <!-- 底层判断出这是一个ref数据,会自动调用x.value -->
           <div>{{ name }}</div>
     <!--      <div>{{ nameObj.name }}</div>-->
     <!-- <div>{{ arr[0] }}  {{ copyArr[0] }}</div> -->
`,
   setup(props, context) {
     // 1. ref
     // const {ref} = Vue
     // // 通过proxy: 'mys' => proxy({value: 'mys'}) 一个响应式引用
     // let name = ref('mys')
     // setTimeout(() => {
     //   name.value = 'lee'
     // }, 2000)
     // return {name}

     // 2.reactive
     const {reactive, toRefs} = Vue
     // 通过proxy: {name: 'mys'} => proxy({name: 'mys'}) 一个响应式引用
     const nameObj = reactive({name: 'mys'})
     setTimeout(() => {
       nameObj.name = 'lee'
     }, 2000)
     // // return {nameObj}

     // 如果用解构,将nameObj中的name值返回,最后不会出现响应式,因为响应式是对象proxy,而此处只是对象的值,不是响应式的
     // const {name} = nameObj
     // 使用toRefs包装能够实现响应式
     // 转换:proxy({name: 'mys'}) => {name: proxy({value: 'mys})},此时name是一个proxy对象,是响应式
     const {name} = toRefs(nameObj)
     return {name}


     // 3.readonly限制响应式引用的修改
     // const {reactive, readonly} = Vue
     // const arr = reactive([123])
     // const copyArr = readonly(arr)
     // setTimeout(() => {
     //   arr[0] = 456
     //   copyArr[0] = 456
     // }, 2000)
     // return {arr, copyArr}
   },
 })
 const vm = app.mount('#root')

6-3 toRef 以及 context 参数

  /**
   * toRefs:如果找不到指定数据,不会给一个默认的响应式引用,而是undefined,就不具备响应式
   * toRef: 如果找不到指定数据,就给一个默认为空的响应式数据
   */
  const app = Vue.createApp({
    template: `
      <div>{{ age }}</div>
<!--      <child app="app"/>-->
      <child @change="handleChange">parent</child>
   `,
    methods: {
      handleChange() {
        console.log('handleChange')
      }
    },
    setup(props, context) {
      const {reactive, toRef} = Vue
      const data = reactive({name: 'mys'})
      const age = toRef(data, 'age')
      setTimeout(() => {
        age.value = 'lee'
      }, 2000)
      return {age}
    },
  })

  /**
   * context
   *    attrs:父组件传递过来的 None-props属性
   *    slots:父组件传递来的插槽  类似:this.$slots
   *    emit:触发事件                this.$emit('change')
   */
  app.component('child', {
    template: `
      <div @click="handleClick">child</div>
   `,
    // mounted() {
    //   console.log(this.$slots)
    //   this.$emit('change')
    // },
    setup(props, context) {
      const {h} = Vue
      const {attrs, slots, emit} = context
      // console.log(attrs) // None-props属性
      // return () => h('div', {}, slots.default()) // parent

      function handleClick() {
        emit('change')
      }
      return {
        handleClick
      }
    },
  })
  const vm = app.mount('#root')

6-4 使用 Composition API 开发TodoList

 // 封装list相关的内容
 const listReactiveEffect = () => {
   const {reactive} = Vue
   const list = reactive([])
   const addItemToList = (item) => {
     list.push(item)
   }
   return {list, addItemToList}
 }

 // 封装inputValue的内容
 const inputReactiveEffect = () => {
   const {ref} = Vue
   const inputValue = ref('123')
   const handleInputValueChange = (e) => {
     inputValue.value = e.target.value
   }
   return {inputValue, handleInputValueChange}
 }

 const app = Vue.createApp({
   // 流程调度中转
   setup() {
     const {list, addItemToList} = listReactiveEffect()
     const {inputValue, handleInputValueChange} = inputReactiveEffect()
     return {
       list, addItemToList,
       inputValue, handleInputValueChange
     }
   },
   template: `
     <div>
     <div>
       <input :value="inputValue" @input="handleInputValueChange"/>
       <button @click="() => addItemToList(inputValue)">提交</button>
     </div>
     <ul>
       <li v-for="(item, index) in list" :key="index">{{ item }}</li>
     </ul>
     </div>
`,
 })
 const vm = app.mount('#root')

6-5 computed方法生成计算属性

 /**
  * computed计算属性
  */
 const app = Vue.createApp({
   setup() {
     const {ref, computed} = Vue
     const count = ref(0)
     const handleClick = () => {
       count.value += 1
     }
     // const countAddFive = computed(() => {
     //   return count.value + 5
     // })
     // computed中也能是一个对象
     let countAddFive = computed({
       get: () => {
         return count.value + 5
       },
       set: (param) => {
         count.value = param - 5
       }
     })
     setTimeout(() => {
       countAddFive.value = 100
     }, 3000)
     return {
       count, handleClick, countAddFive
     }
   },
   template: `
     <div>
     <span @click="handleClick">{{ count }}</span> -- {{countAddFive }}
     </div>
`,
 })
 const vm = app.mount('#root')

6-6 watch 和 watchEffect 的使用和差异性

 const app = Vue.createApp({
   setup() {
     const {ref, reactive, watch, toRefs, watchEffect, watchEffectRefs} = Vue
     // const name = ref('mys')
     const nameObj = reactive({
       name: 'mys',
       englishName: 'lee'
     })
     const {name} = toRefs(nameObj)
     /**
      * watch监听器
      * 1.具备一定的惰性 lazy 第一次不执行,发生变化才执行
      * 2.参数currentValue/preValue:当前值和之前值
      * 3.当监听reactive类型的数据时,需要使用箭头函数:() => nameObj.name
      * 4.可以用一个监听器来监听多个数据的变化
      */
     // 1.监听ref类型数据
     // watch(name, (currentValue, preValue) => { }
     // 2.监听reactive类型数据
     // watch(() => nameObj.name, (currentValue, preValue) => {
     //   console.log(currentValue, preValue)
     // })
     // 3.监听多个数据
     watch([() => nameObj.name, () => nameObj.englishName], ([curName, curEng], [preName, preEng]) => {
       console.log('watch', curName, preName, '--', curEng, preEng)
     }, {
       immediate: true // 把watch变成立即执行
     })

     /**
      * watchEffect监听器
      * 1.立即执行,没有惰性 immediate
      * 2.不需要传递监听的内容,会自动感知代码依赖,不需要传递很多参数,只需要传递一个回调函数
      * 3.不能获取之前数据的值
      *
      * 应用:监听Ajax请求;异步请求
      * watch watchEffect都可以通过定时器停止
      */
     const stop = watchEffect(() => {
       console.log('watchEffect', nameObj.name, nameObj.englishName)
       setTimeout(() => {
         stop()
       }, 3000)
     })

     return {name, nameObj}
   },
   template: `
     <div>
     name:<input v-model="nameObj.name "/>
     <div>{{ nameObj.name }}</div>

     englishName:<input v-model="nameObj.englishName "/>
     <div>{{ nameObj.englishName }}</div>
     </div>
`,
 })
 const vm = app.mount('#root')

6-7 生命周期函数的新写法

 const app = Vue.createApp({
   /**
    * 生命周期函数
    * beforeMount => onBeforeMount
    * mounted => onMounted
    * beforeUpdate => onBeforeUpdate
    * beforeUnmounted => onBeforeUnmounted
    * unmounted => onUnmounted
    * 没有beforeCreate/create,直接写在setup中即可
    * 新的生命周期函数:
    * onRenderTracked:每次渲染后重新收集响应式依赖
    * onRenderTriggered:每次触发页面重新渲染时自动执行
    */
   setup() {
     const {
       ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmounted, onUnmounted,
       onRenderTracked, onRenderTriggered
     } = Vue
     const name = ref('mys')
     onBeforeMount(() => {
       console.log('onBeforeMount')
     })
     onMounted(() => {
       console.log('onMounted')
     })
     onBeforeUpdate(() => {
       console.log('onBeforeUpdate')
     })
     onUpdated(() => {
       console.log('onUpdated')
     })
     onRenderTracked(() => {
       console.log('onRenderTracked')
     })
     onRenderTriggered(() => {
       console.log('onRenderTriggered')
     })
     handleClick = () => {
       name.value = 'lee'
     }
     return {name, handleClick}
   },
   template: `
     <div @click="handleClick">
     {{name}}
     </div>
`,
 })
 const vm = app.mount('#root')

6-8 Provide,Inject,模版 Ref 的用法

 /**
  * provide inject
  */
 const app = Vue.createApp({
   setup() {
     const {provide, ref, readonly} = Vue
     const name = ref('mys')
     provide('name', readonly(name)) // 不让子组件修改name
     provide('changeName', (value) => {
       name.value = value
     })
     return {}
   },
   template: `
     <div>
      <child/>
      <domTest/>
     </div>
`,
 })

 app.component('child', {
   setup() {
     const {inject} = Vue
     // 获取父组件传来的name,如果没有,默认值为hello
     const name = inject('name', 'hello')
     const changeName = inject('changeName')
     const handleClick = () => {
       // 如果子组件想要修改数据,需要告诉父组件,让父组件来修改 => 单向数据流
       // 下面的方法虽然可以修改,但是不推荐使用
       // name.value = 'lee'
       changeName('lee')
     }
     return {name, handleClick}
   },
   template: `
     <div @click="handleClick">{{ name }}</div>
`
 })

 /**
  * Composition API语法下  获取真实的DOM元素节点
  * 1.在template中定义一个ref引用 ref="hello"
  * 2.在setup中定义一个与ref名字相同的常量并且等于空 const hello = ref(null)
  * 3.在生命周期函数onMounted中获取dom元素
  * 4.return常量 return {hello}
  */
 app.component('domTest', {
   setup() {
     const {ref, onMounted,} = Vue
     const hello = ref(null)
     onMounted(() => {
       console.log(hello.value)
     })
     return {hello}
   },
   template:`
    <div>
       <div ref="hello">hello world</div>
    </div>
   `
 })
 const vm = app.mount('#root')

7 Vue 项目开发配套工具讲解

7-1 VueCLI 的使用和单文件组件

1、安装node npm nrm

(1)Node环境安装

下载地址:https://nodejs.org/zh-cn/ (下载长期支持版本LTS)

安装完成,在终端输入:node -v 查看版本号

(2)NPM:Node Package Manager

查看版本:npm -v

(3)NRM(Npm Registry Manager )是npm的镜像源管理工具源切换,找到当前最快的安装源**

安装:npm install -g nrm

NRM安装不成功

如果安装nrm出现下面信息,安装失败,解决办法参考:https://blog.csdn.net/blue_698/article/details/117874021

在这里插入图片描述

  1. 执行:npm config list -l,出现下面结果

在这里插入图片描述

  1. 执行npm cache clean --force清除缓存

  2. 执行nrm ls出现下面结果即解决

在这里插入图片描述

NRM常用命令

测试源的速度:nrm test npm

切换源:nrm use taobao

查看可用源:nrm ls

查看当前源:nrm current

增加定制源:nrm add imooc http://192.168.1.100:6666

删除源:nrm del imooc

2、删除老版本工具

如果之前使用过vue,可以通过下面两个命令删除:

npm uninstall vue-cli -g
yarn global remove vue-cli

3、安装vue新版本工具

可以快速搭建vue工程的一个工具:npm install -g @vue/cli,注意:用管理员身份打开终端

4、创建项目

在这里插入图片描述

5、也可以通过IDE来打开项目并启动,这里我用的是webstorm

在这里插入图片描述

6、项目结构说明

在这里插入图片描述

main.js

import { createApp } from 'vue' // 创建Vue的实例 app
import App from './App.vue' // app来自当前文件夹下的App.vue

createApp(App).mount('#app')

App.vue

<!--
  单文件组件:App.vue文件代表一个组件
    1.template 模板
    2.script 核心逻辑
    3.style 样式
-->
<template>
  <img alt="Vue logo" src="./assets/logo.png">
  <HelloWorld msg="Welcome to Your Vue.js App"/>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App', // 根组件
  components: { // 局部组件
    HelloWorld
  }
}
</script>

<style>

</style>

components/HelloWorld.vue

<template>
  <h1>{{ msg }}</h1>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  }
}
</script>

<style>

</style>

7-2 使用单文件组件编写 TodoList

1、在components下写ListItem.vue子组件

<template>
  <li>{{ msg }}</li>
</template>

<script>
export default {
  name: "ListItem",
  props: {
    msg: String
  }
}
</script>

<style scoped>

</style>

2、在单文件组件App.vue中写:template script style

<template>
  <div>
    <input v-model="inputValue"/>
    <button class="button" @click="handleAddItem">提交</button>
  </div>
  <ul>
    <list-item
        v-for="(item, index) in list"
        :key="index"
        :msg="item"
    />
  </ul>
</template>

<script>
import {reactive, ref} from 'vue'
// 引入ListItem组件
import ListItem from './components/ListItem';

export default {
  name: 'App', // 根组件
  // 注册ListItem组件
  components: {ListItem},
  setup() {
    const inputValue = ref('')
    const list = reactive([])
    const handleAddItem = () => {
      list.push(inputValue.value)
      inputValue.value = ''
    }
    return {inputValue, list, handleAddItem}
  },
}
</script>

<style>
.button {
  margin-left: 20px;
}
</style>

7-3 Vue-Router 路由的理解和使用

1、创建项目

步骤上面类似,需要修改:

在这里插入图片描述

2、哈希路由:根据不同的URL展示不同的效果

在这里插入图片描述

3. 流程

1.通过入口main.js,引入router路由 
2.在router文件夹下定义路由 	=>将不同的组件与不同的路径相对应
3.在views下写各个路由所对应的组件内容
4.在App.vue中引入路由跳转及其组件内容
5.通过app实例运行项目

main.js

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

// 哈希路由:根据URL的不同,展示不同的效果
createApp(App).use(router).mount('#app')

router/index.js

import { createRouter, createWebHashHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'

const routes = [
  // 访问不同的路径,展示的组件不同
  {
    path: '/',
    name: 'home',
    component: HomeView
  },
  {
    path: '/about',
    name: 'about',
    // 异步加载路由:访问的时候再去加载
    // 缺点:可能会有卡顿  具体怎么选择根据项目来决定
    component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
  },
  {
    path: '/login',
    name: 'login',
    component: () => import(/* webpackChunkName: "about" */ '../views/LoginView.vue')
  }
]

const router = createRouter({
  history: createWebHashHistory(),
  routes
})

export default router

views/LoginView.vue

<template>
  <div class="login">
    <h1>LoginPage</h1>
  </div>
</template>

App.vue

<template>
  <nav>
    <!-- router-link:跳转路由的标签 -->
    <router-link to="/">Home</router-link> |
    <router-link to="/about">About</router-link> |
    <router-link to="/login">Login</router-link>
  </nav>
  <!-- router-view:展示当前路由对应的组件内容 -->
  <router-view/>
</template>

<style>

</style>

7-4 VueX 的语法详解

1、创建项目
在这里插入图片描述

2、流程

(1)获取数据

1.通过main.js引入router、store
2.在store文件夹下创建数据仓库,用来存放全局数据,在state中定义数据
3.在views中通过computed计算属性来获取数据并展示在页面上
4.通过app实例运行项目

(2)修改数据

1.通过dispatch方法,派发一个action,名为change
2.感知到change的action,执行store中的actions下面的change方法
3.commit提交一个名为change的数据改变
4.mutation感知到提交的change改变,执行change方法,改变数据

dispatch 和 actions关联
commit 和 mutation关联

main.js

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

createApp(App).use(store).use(router).mount('#app')

store/index.js

import { createStore } from 'vuex'

// VueX:数据管理框架
// Vue 创建了一个全局唯一的仓库,用来存放全局的数据
export default createStore({
  state: {
    name: 'mys'
  },
  getters: {
  },
  // 潜移默化的约定:mutations中只能写同步的代码,不能写异步的。异步代码通常放到actions中
  mutations: {
    // 第四步:mutation被执行
    change(state, str) {
      // 第五步:在mutation里面修改数据
      state.name = str
    }
  },
  actions: {
    // 第二步:store感知到触发了一个change的action,执行change方法
    change(store, str) {
      // 第三步:提交一个commit,触发一个mutation
      setTimeout(() => {
        this.commit('change', str)
      }, 2000)
    }
  },
  modules: {
  }
})

views/AboutView.vue

<template>
  <div class="about">
    <h1 @click="handleClick">This is an about page</h1>
    <h1>{{ myName }}</h1>
  </div>
</template>

<script>
export default {
  name: 'AboutView',
  computed: {
    myName() {
      return this.$store.state.name
    }
  },
  methods: {
    /**
     * 改变store中的数据步骤:
     * 1.通过dispatch方法,派发一个action,名为change
     * 2.感知到change的action,执行store中的actions下面的change方法
     * 3.commit提交一个名为change的数据改变
     * 4.mutation感知到提交的change改变,执行change方法,改变数据
     */
    handleClick() {
      // 第一步:想要改变数据,vue要求第一步必须派发一个action
      // change:名字 hello world:参数
      this.$store.dispatch('change', 'hello world')
    }
  },
}
</script>

App.vue

<template>
  <nav>
    <router-link to="/">Home</router-link> |
    <router-link to="/about">About</router-link>
  </nav>
  <router-view/>
</template>

<style>

</style>

7-5 CompositionAPI 中如何使用 VueX

不同之处:在setup()中通过VueX提供的useStore获取全局数据对象
views/AboutView.vue

<template>
  <div class="about">
    <h1 @click="handleClick">This is an about page</h1>
    <h1>{{ name }}</h1>
  </div>
</template>

<script>
import {useStore} from 'vuex'
import {toRefs} from 'vue'

export default {
  name: 'AboutView',
  setup() {
    // 通过useStore获取全局数据对象
    const store = useStore()
    const {name} = toRefs(store.state)
    const handleClick = () => {
      store.dispatch('change', 'hello')
    }
    return {
      name, handleClick
    }
  }
}
</script>

7-6 使用 axios 发送ajax 请求

1、安装axios工具

npm install axios --save

2、在store/index.js发请求

actions: {
    change(store) {
      axios.get('https://www.fastmock.site/mock/ae8e9031947a302fed5f92425995aa19/jd/api/user/register')
        .then((response) => {
          const msg = response.data.message
          console.log(msg)
          store.commit('change', msg)
        })
    }
  },

8 “京东到家”项目首页开发

8-1 工程初始化

在这里插入图片描述

8-2 工程目录代码简介及整理

8-3 基础样式集成及开发模拟器的使用

1、安装normalize.css文件

npm install normalize.css --save

2、src下新建style文件夹,创建base.css

base.css

/* rem与px转换:1rem = 1 html font-size = 100px */
html {
    font-size: 100px;
}

3、在main.js中引入

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import 'normalize.css'
import './style/base.css'

createApp(App).use(store).use(router).mount('#app')

4、将浏览器调整为移动端模式

未完…

1. Vue3入门--黑马程序员实战笔记
qq_52338998的博客
05-15 563
通过 Counter 案 体验Vue3新引入的组合式API。
vue实战尤雨溪pdf_「速围」尤雨溪详细介绍 Vue 3 的最新进展
weixin_39841572的博客
11-22 1530
作者:Evan You 转发链接:https://github.com/vuejs/rfcs/issues/183译者:Echa 攻城狮前言近日,Vue.js 作者尤雨溪在 GitHub 上介绍了 Vue 3 的最新进展。我们的许多用户都在问这个问题:Vue 3何时准备就绪?我们没有给出确切的答案,因为预测软件交付时间几乎是不准确的。作为一个非营利性项目,我们希望专注于编写优秀的软件,而不是按时完...
2024年前端最全Vue3 学习笔记 —— (一)深入理解组合式 API,2024年最新阿里P8架构师前端大厂面试题总结
2401_84616399的博客
05-12 623
回到题目,如果你真想检验一个人的水平。第一步先考察一下基本的编程基础,问几个基本的编程问题,可以和前端相关也可以无关。比如垃圾收集大致是怎么做的,setTimeout 大致做了什么(说会在另一个线程里执行回调的直接毙掉)。第二步考察一下知识面,问问http、tcp的基本知识,dns是怎么工作的,或者常用框架的实现原理,看看候选人是不是除了自己的一亩三分地什么都不关心。
全网最全的vue3入门教程『图文并茂』
linwu的博客
07-26 1605
Vue 3 是一个流行的开源JavaScript框架,用于构建用户界面和单页面应用。它带来了许多新特性和改进,包括更好的性能、更小的打包大小、更好的TypeScript支持、全新的组合式 API,以及一些新的内置组件。你可以创建自定义的hooks来复用代码。一个自定义hook就是一个函数,它可以使用其他的响应式数据和组合式API。import {setup() {你可以使用customRef函数来创建一个自定义的响应式引用。这允许你控制和观察当引用的值发生变化时的行为。
前端VUE3+Vite -- 框架搭建
寿春
06-02 4592
卸载 vue – 安装 vue Vite vite 官网学习开始构建越来越大型的应用时,需要处理的 代码量也呈指数级增长。包含数千个模块的大型项目相当普遍。我们开始遇到性能瓶颈 —— 使用 开发的工具通常需要很长时间(甚至是几分钟!)才能启动开发服务器,即使使用 ,文件修改后的效果也需要几秒钟才能在浏览器中反映出来。如此循环往复,迟钝的反馈会极大地影响开发者的开发效率和幸福感。1.旨在利用生态系统中的新进展解决上述问题:浏览器开始原生支持 模块,且越来越多 2. 工具使用编译型语言编写。...
[零基础学习Vue3+webpack项目]的vue3代码的开发编译及其启动项目服务的详解(3)
月影WEB的博客
05-10 843
在[零基础学习Vue+webpack]之vue+webpack项目的基础依赖项的安装详解(1)https://blog.csdn.net/qq_34297287/article/details/124656994?spm=1001.2014.3001.5501 和 [零基础学习Vue+webpack]之vue3+webpack项目的webpack对vue3进行编码打包的详解(2)https://blog.csdn.net/qq_34297287/article/details/124677916?spm
Vue3 系统入门项目实战.zip
01-02
Vue3,springboot,element-ui使用技巧,实战应用开发小系统参考资料,源码参考。 详细介绍了一些Qt框架的各种功能和模块,以及如何使用Qt进行GUI开发、网络编程和跨平台应用开发等。 适用于初学者和有经验的开发者...
VUE入门学习项目资料与学习笔记.zip
02-12
主要是本人在学习vue过程中总结的学习心得和代码示。包括Vue的环境搭建、常用指令、属性、组件、vue-router、axios以及项目的简单讲解。
vue 3.0学习笔记(从入门到精通)
10-07
vue 3.0自我学习整理的笔记,有很大的参考价值,包括了所有的知识点,vue的开发环境搭建、组件开发、数据状态管理、vue route、组件之前数据传递等有关知识点
vue项目实战 vue项目实战
01-20
vue项目实战vue项目实战vue项目实战vue项目实战vue项目实战vue项目实战vue项目实战vue项目实战vue项目实战vue项目实战vue项目实战vue项目实战vue项目实战vue项目实战vue项目实战vue项目实战vue项目实战vue项目实战...
基于Vue的英语笔记学习管理系统设计源码
最新发布
05-26
本项目是基于Vue的英语笔记学习管理系统设计源码,包含38个文件,其中包括14个PNG图片文件、6个JavaScript文件、4个CSS文件、2个Markdown文档、2个JSON文件、2个Vue文件、1个gitignore文件、1个LICENSE文件和1个HTML...
WebApp开发自学记录:Vue3.x应用的创建、使用
allenkjjf的博客
01-14 363
WebApp开发自学记录:介绍Vue3.x应用的创建、使用
从零创建完整的个人项目 (webapck+vue3+element vite+vue3+VantUI)
Beatingworldline的博客
02-15 802
参考 安装node 安装vue-cli脚手架 npm i -g @vue/cli-init 建立项目文件夹执行脚本 vue create projectname 手动选择需要的插件 vuex、router、babel 、vue版本、Css预处理器 等等配置 ——项目创建过程已经集成化了 很简单 待补全: 引入elementUI 配置axios与不同环境的访问地址 登录 环境变量 webpack脚本 vue.config.js相关配置 ...
免费分享《web前端Vue3训练营》视频教程
qq_43290778的博客
04-14 123
Vue3训练营共包含40个视频教程,涉及Vue框架的各个方面,包括Vue生命周期、事件传递、组件传递数据、响应式原理、异步组件、模板引用等等。Vue3训练营是一系列深入浅出的视频教程,涵盖了web前端开发中Vue框架的所有知识和技能。通过学习Vue3训练营,你将成为一名高端的前端开发工程师,并为未来的职业生涯打下坚实的基础。您可以从零开始学习Vue3,从而深入了解Vue3的各个方面,并掌握在实际开发中的应用方法。不管是初学者还是有经验的前端开发工程师,Vue3训练营都是一个不可错过的学习机会。
Vue3开发学习笔记:使用axios post请求WebService
JustWantToFly的博客
04-14 4525
Vue3中使用axios.post请求获取WebService返回的数据
Vue.js 3.0 企业级管理后台开发实战 基于Element Plus
u011645099的博客
10-08 2019
Vue.js 是前后端分离开发的主流技术之一,它是一套构建用户界面的渐进式框架,以数据 驱动视图和组件化的思想构建,采用自底向上、增量开发的设计,其核心库只关注视图层。同 时,Vue.js 完全有能力驱动采用单文件组件和 Vue.js 生态系统支持的库开发的复杂单页应用。
小兔鲜Vue3 项目学习笔记Day02
不被定义的程序媛写的博客儿
05-21 1092
文章目录Pinia1.使用2. pinia-计数器案3. getters实现4. 异步action5. storeToRefsx 数据解构保持响应式6. pinia 调试项目起步1.项目初始化和git管理2. 使用ElementPlus3. ElementPlus 主题色定制4. axios 基础配置5. 路由设计6. 静态资源初始化和 Error lens安装7.scss自动导入8. Layout静态模板结构搭建9. Layout字体图标引入10.Layout一级导航渲染11. layout - 吸顶导
Vue 笔记【视频参考尚硅谷】持续更新2022/3/22...
记录成长的小博客
03-19 1047
Vue 第 1 章:Vue 核心 视频学习地址: https://www.bilibili.com/video/BV1Zy4y1K7SH 学习配套资源(源代码、静态页面等): 链接:https://pan.baidu.com/s/1521h1ATCK-ccmyrCNb2HVA 提取码:612x 1.1. Vue 简介 1.1.1. 官网 英文官网: https://vuejs.org/ 中文官网: https://cn.vuejs.org/ 1.1.2. 介绍与描述 1. 动态构建用
Vue3 学习笔记 ——
m0_69940800的博客
05-04 209
*/ export default { name: ‘App’, setup() { const counter = ref(1000) // console.log(counter.value) const increment = () => { counter.value += 1; } const decrement = () => { counter.value -= 1; } // 必须 return,外部才能拿到值 return { counter, increment,decrem
Vue3的从入门到实战
09-26
Vue3的从入门到实战可以通过以下几个步骤完成: 1. 搭建脚手架:使用Vue CLI工具创建一个新的Vue3项目。这个工具将为你提供一个基本的项目结构和配置。 2. 学习基本语法:了解Vue3的基本语法和组件概念。学习如何定义和使用组件、如何进行数据绑定、如何处理用户输入等。 3. 掌握组合式API:与Vue2不同,Vue3引入了组合式API,它将data、methods等组合在一起,更易于理解和使用。学习如何使用setup函数创建组件、如何使用reactive函数进行响应式数据处理等。 4. 使用Vue Router进行路由管理:学习如何使用Vue Router进行页面导航和路由管理。了解如何配置路由、定义路由组件、进行路由跳转等。 5. 使用Vuex进行状态管理:学习如何使用Vuex进行全局状态管理。了解如何定义和使用store、如何进行状态的读取和修改等。 6. 使用Element Plus进行UI设计:了解如何使用Element Plus库进行页面的UI设计和组件的使用。掌握如何使用常用的UI组件,如按钮、表单、表格等。 7. 使用axios进行数据交互:学习如何使用axios库进行与后端API的数据交互。了解如何发送GET、POST等请求,如何处理响应数据等。 8. 实战项目:通过实际的项目练习,将以上所学的知识应用到实践中。可以选择一个简单的项目,利用Vue3的各种功能进行开发。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
写文章

热门文章

  • 解决办法:ssh: connect to host master port 22: Connection timed out 34678
  • unused import statement解决方法 31092
  • Resourcemanager启动失败解决方法 12505
  • 微信公众平台-测试号网页授权-获取openid方法 11407
  • could not be created: Error: EPERM: operation not permitted, open ‘D:\Program Files (x86)\nodejs 9867

分类专栏

  • Java 47篇
  • 后端面经 4篇
  • 随记 1篇
  • 数据结构与算法 17篇
  • LeetCode 27篇
  • 前端 7篇
  • 编程语言 7篇
  • 机器学习 15篇
  • 数据库 8篇
  • 大数据 26篇
  • Linux 10篇
  • 计算机网络 3篇
  • 数据挖掘 6篇

最新评论

  • javaweb课堂笔记(二)

    轻裾12312: 感谢学姐!

  • 如何提升自己?

    电工渣: 无限进步~~

  • 统计学习方法代码实现一 —— 最小二乘法

    ᰔᩚ.834: 为什么我和你的代码完全一样 但是图形有个别点不一样呢?

  • 微信公众平台-测试号网页授权-获取openid方法

    william_ch3n: 自己配置测试的时候一直报错 “redirect_uri域名与后台配置不一致,错误码10003”,看了这个文章才知道需要在“网页授权获取用户基本信息”的地方再次配置域名。。 希望能帮到大家!

  • javaweb课堂笔记(二)

    qmys: 不客气哦 加油!

大家在看

  • 群体优化算法----多乌鸦搜寻算法介绍,找多目标函数组解,Pareto前沿 49

最新文章

  • HashMap知识点总结
  • 如何提升自己?
  • 后端面经学习自测(三)
2023年25篇
2022年31篇
2021年11篇
2019年58篇
2018年78篇

目录

目录

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

深圳SEO优化公司盘锦设计公司网站忻州网络广告推广多少钱南平网站优化按天扣费信阳网络营销多少钱黑河网站关键词优化公司阳泉模板推广公司自贡网站优化排名价格沧州外贸网站设计多少钱南昌网站设计价格长治网站推广系统推荐大连网站推广方案多少钱海西网站制作设计哪家好永州网站关键词优化垦利企业网站改版报价池州网站建设设计多少钱温州关键词排名包年推广报价重庆网站seo优化本溪关键词按天扣费多少钱淮南企业网站改版多少钱平湖网站优化按天收费哪家好甘南建站报价大理网络推广多少钱巢湖品牌网站设计开封建站价格长治企业网站制作价格邯郸网站制作设计多少钱飞来峡设计公司网站报价伊春企业网站建设多少钱白银网站搜索优化多少钱嘉兴关键词按天收费价格歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化