Vue 3 学习笔记 - 基础

Vue 3 学习笔记 - 基础

主要参考 Vue 3 教程

基础

引入

<script src="https://unpkg.com/vue@next"></script> <!--开发环境-->
<script src="https://unpkg.com/vue@3.2.33/dist/vue.global.prod.js"></script> <!--生产环境 最好指定版本号-->

响应式渲染

<div id="counter">
  Counter: {{ counter }}
  <br><!--甚至可以是一个表达式-->
  3+3={{3+3}}
</div>
const Counter = {
  data: ()=>({counter: 0}) //一个函数,返回可以在 {{<表达式>}} 里使用的变量,直接成为 this 的属性(可以被外部函数改变)
    					 //(实际上以 $data 的形式存储在组件实例中)
}
Vue.createApp(Counter).mount('#counter') //创建组件,绑定 id=counter 的元素,只会绑定第一个

v-bind

<div id="bind-attribute">
  <span v-bind:title="message"> <!--把 message 变量绑定到这个 div 对象的 title 属性 (attribute) 上-->
    鼠标悬停几秒钟查看此处动态绑定的提示信息!
  </span>
  <span :title="message"> <!--可以使用简写形式 `:<属性>` ,或许支持几乎所有 HTML 属性-->
    鼠标悬停几秒钟查看此处动态绑定的提示信息!
  </span>
</div>
const AttributeBinding = {data: ()=>({message: 'You loaded this page on ' + new Date().toLocaleString()})}
Vue.createApp(AttributeBinding).mount('#bind-attribute')

参考 HTML 属性参考

v-on

<div id="event-handling">
  <p>{{ message }}</p>
  <button v-on:click="reverseMessage">反转 Message</button>  
    <!--把 reverseMessage 方法绑定到这个 button 对象的 onclick 事件上-->
  <button @click="reverseMessage">反转 Message</button> <!--或者使用简写形式 `@事件`-->
  <button v-on:[onname]="reverseMessage">反转 Message</button> <!--v-on 和 v-bind 可以使用动态参数-->
</div>

const EventHandling = {
  data:
    () =>({
      message: 'Hello Vue.js!',
      onname: 'click'
    }),
  methods: {
    reverseMessage() //切不可用箭头函数 因为箭头函数的作用域是全局 this 不是这个对象了
      {this.message = this.message.split('').reverse().join('')}
  }
}

Vue.createApp(EventHandling).mount('#event-handling')

参考 HTML 事件参考手册GlobalEventHandlers

v-model

<div id="two-way-binding">
  <p>{{ message }}</p>
  <input v-model="message" /> <!--输入框-->
    <!--双向绑定:不仅改变 message 变量值时 inputbox 内容会变,改变 inputbox 内容 message 变量也会变-->
  <input :value="message" @input="message = $event.target.value"> <!--是这个的语法糖-->
</div>
const TwoWayBinding = {data: ()=>({message: 'Hello Vue!'})}
Vue.createApp(TwoWayBinding).mount('#two-way-binding')

v-if

<div id="conditional-rendering">
    <button @click="Switch">切换</button>
    <h1 v-if="awesome">Vue is awesome!</h1>
</div>
const ConditionalRendering  = {
  data: ()=>( { awesome: true } ),
  methods: { Switch(){ this.seen = !this.seen } }
}
Vue.createApp(ConditionalRendering ).mount('#conditional-rendering')

v-for

<div id="list-rendering">
  <ol><li v-for="todo in todos">
      {{ todo.text }}
  </li></ol>
</div>
const ListRendering = {
  data:() =>({
      todos: [
        { text: 'Learn JavaScript' },
        { text: 'Learn Vue' },
        { text: 'Build something awesome' }
      ]
    })
}

Vue.createApp(ListRendering).mount('#list-rendering')

应用实例

是时候回过头看看组件的创建 Vue.createApp

创建

const app = Vue.createApp({
  data:()=>({}), //参考 响应式渲染 例子
  methods:{/* 一些方法 */}, //参考 `v-on` 例子
  computed:{}, //参考 计算属性 例子
  watch:{} //参考 监听器 例子
})//注册“全局”组件

//简单的例子
app.component('SearchInput', SearchInputComponent)
app.directive('focus', FocusDirective)
app.use(LocalePlugin)
//或者链式
const app = Vue.createApp({})
  .component('SearchInput', SearchInputComponent)
  .directive('focus', FocusDirective)
  .use(LocalePlugin)

计算属性 computed

const app = Vue.createApp({
  data:()=>({}),
  computed: { // 计算属性 与 method 的区别在于 它是有缓存的,加快静态数据读取速度 (即它是“属性”)
         	  // 若使用了Vue的`响应式依赖`时会自动更新,而其他方法不会自动更新 如 `Date.now()`
    publishedBooksMessage() {
      return this.author.books.length > 0 ? 'Yes' : 'No'
    },
    fullName: { // 一个完整的计算属性包括 getter 和 setter
      // getter 事实上之前的一个计算属性的例子 getter 部分
      get() {
        return this.firstName + ' ' + this.lastName
      },
      // setter 可以用 app.fullName = 'Alice Bob' 赋值
      set(newValue) {
        const names = newValue.split(' ')
        this.firstName = names[0]
        this.lastName = names[names.length - 1]
      }
    }
  }
})

监听器 watch

<div id="watch-example">
    Ask a yes/no question:  <input v-model="question" />
  <br>
  {{ answer }}
</div>
<script src="https://unpkg.com/axios@0.12.0/dist/axios.min.js"></script>  
const watchExampleVM = Vue.createApp({
  data: () => ({
    question: "",
    answer: "Questions usually contain a question mark. ;-)",
  }),
  watch: { // 监听器,提供了一个更通用的方法来响应数据的变化
    // 最好在需要在数据变化时执行异步或开销较大的操作时使用 通常可以用计算属性代替
    question(newQuestion, oldQuestion) { //每当 question 发生变化时,该函数将会执行
      if (newQuestion.indexOf("?") > -1) {
        this.getAnswer();
      }
    },
  },
  methods: {
    getAnswer() {
      this.answer = "Thinking...";
      axios
        .get("https://yesno.wtf/api")
        .then((response) => {
          this.answer = response.data.answer;
        })
        .catch((error) => {
          this.answer = "Error! Could not reach the API. " + error;
        });
    },
  },
}).mount("#watch-example");

一个 compute 的重写例子:

  computed: {
    question: {
      get() {return this.ques;}, // 在只有一个变量且是本身的情况下可能用 watch 更好
      set(newValue) {
        this.ques = newValue;
        if (newValue.indexOf("?") > -1) {
          this.getAnswer();
      }
      },
    },
  },

再看元素属性 (attribute)

v-bind 之与 Class/Style 绑定

:class 可以是一个对象

<div :class="{ active: isActive }"></div>
<div :class="classObject"></div>
data:()=>({
    isActive: true,
    classObject:{
        active: true,
        'text-danger': false
    }
})

也可以是一个数组

<div :class="[activeClass, errorClass]"></div>
<!--甚至可以组合-->
<div :class="[{ active: isActive }, errorClass]"></div>
data:()=>({
    isActive: true,
    activeClass: 'active',
    errorClass: 'text-danger'
})

:style 是一个对象

<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div> <!--使用驼峰命名法-->
<div :style="{ 'color': activeColor, 'fontSize': fontSize + 'px' }"></div> <!--或者短横线分隔 要有引号-->
<div :style="styleObject"></div> <!--使用对象-->
data:()=>({
  isActive: true,
  activeClass: 'active',
  errorClass: 'text-danger'
  styleObject: {
    color: 'red',
    fontSize: '13px'
  }
})

也可以糅合数组

<div :style="{styleObj1, display: ['-webkit-box', '-ms-flexbox', 'flex']}"></div> <!--数组内选取最后一个支持浏览器的-->
<div :style="[styleObj1, styleObj2]"></div> <!--相同 style 会覆盖 (override)-->

v-if 与逻辑链

  • if-else
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
  • else if
<div v-if="type === 'A'">
  A
</div>
<div v-else-if="type === 'B'">
  B
</div>
<div v-else-if="type === 'C'">
  C
</div>
<div v-else>
  Not A/B/C
</div>

v-show

用法类似 v-if ,与之不同 参考

v-show 不支持 <template> 元素,也不支持 v-else

如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

v-for 之列表渲染

<ul id="array-rendering">
  <li v-for="item in items">
    {{ item.message }}
  </li>
</ul>

如果 items 是数组,会遍历数组;如果是对象,会遍历属性值

或者使用 (value, name) in myObject 来同时使用键名和键值

或者使用 (value, name, index) in myObjec 来使用键名、键值和索引

推荐使用 :key ,在同一元素中的 key 必须唯一, key 变化会触发替换/重渲染/重排序,因此设置确定的 key 可以防止重复渲染,例如

<div v-for="item in items" :key="item.id"><!-- 内容 --></div>

永远不要在一个元素上同时使用 v-ifv-for 参考

v-on 与 事件修饰符

  • 事件修饰符 需要者自会用到

  • 按键修饰符

    <input @keyup.enter="submit" />
    

    监听键盘按键 enter 在按下时调用 submit ,可以用 Key Values 中所有键名转换成短横线分隔格式

    Vue对下列有特殊修饰符:

    • 最常用的键的别名 .enter , .tab , .esc , .space , .up , .down , .left .right , .delete (捕获“删除”和“退格”键)
    • 系统修饰键 .ctrl , .alt , .shift , .meta 可以串用
    • .exact 修饰符 有且仅有修饰的按键被按下时执行
    • 鼠标按键 .left , .right , .middle

v-model

v-model 在内部为不同的输入元素使用不同的 property 并抛出不同的事件:

  • text 和 textarea 元素使用 value property 和 input 事件;
  • checkbox 和 radio 使用 checked property 和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件。
  • 下拉列表

    <div id="select">
        <select v-model="selected">
          <option value="A被选">A</option>
          <option value="B被选">B</option>
          <option value="C被选">C</option>
        </select>
        <span>Selected: {{ selected }}</span>
    </div>
    
    const Selected = {data: ()=>({selected: ''})}
    Vue.createApp(Selected).mount('#select')
    
  • 单选

    <div id="pick">
      <input type="radio" id="small" value="small_value" v-model="picked">
      <label for="small">small</label>
      <br>
      <input type="radio" id="big" value="big_value" v-model="picked">
      <label for="big">big</label>
      <br>
      <span>Picked: {{ picked }}</span>
    </div>
    
    const Selected = {data: ()=>({picked: ''})}
    Vue.createApp(Selected).mount('#pick')
    
  • 复选

    <div id="check">
        <input type="checkbox" id="one" value="value_one" v-model="checkedNames">
        <label for="one">选项一</label>
        <input type="checkbox" id="two" value="value_two" v-model="checkedNames">
        <label for="two">选项二</label>
        <input type="checkbox" id="three" value="value_three" v-model="checkedNames">
        <label for="three">选项三</label>
        <br>
        <span>Checked names: {{ checkedNames }}</span>
    </div>
    
    const Selected = {data: ()=>({checkedNames: []})}
    Vue.createApp(Selected).mount('#check')
    

更多请参考 表单输入绑定

组件

要注意: HTML 内使用短横杠分割, Js 内使用驼峰命名

创建

<div id="components-demo">
  <button-counter></button-counter>
</div>
// 创建一个Vue 应用
const app = Vue.createApp({})
// 在app内(全局)定义一个名为 button-counter 的组件
app.component('button-counter', {
  data:()=>({count: 0}),
  template: `
    <button @click="count++">
      You clicked me {{ count }} times.
    </button>`
    // 我们采用反引号包裹的模板字符串作为模板
})
app.mount('#components-demo')

自定义属性 (attribute)

<div id="blog-post-demo" class="demo">
  <blog-post title="My journey with Vue"></blog-post>
  <blog-post title="Blogging with Vue"></blog-post>
  <blog-post title="Why Vue is so fun"></blog-post>
</div>
const app = Vue.createApp({})

app.component('blog-post', {
  props: ['title'], // 这里定义一个 `title` 的属性
  template: `<h4>{{ title }}</h4>` 
})

app.mount('#blog-post-demo')

bind 结合

<div id="blog-posts-demo">
  <blog-post v-for="post in posts" :key="post.id" :title="post.title"></blog-post>
</div>
const App = {
  data:() =>({posts: [
    { id: 1, title: 'My journey with Vue' },
    { id: 2, title: 'Blogging with Vue' },
    { id: 3, title: 'Why Vue is so fun' }
  ]})
}
const app = Vue.createApp(App)

app.component('blog-post', {
  props: ['title'],
  template: `<h4>{{ title }}</h4>`
})

app.mount('#blog-posts-demo')

自定义事件

<div id="blog-posts-events-demo" class="demo">
  <div :style="{ fontSize: postFontSize + 'em' }">
    <blog-post
        v-for="post in posts"
        :key="post.id"
        :title="post.title"
        @enlarge-text="postFontSize += 0.1" <!--这里定义了一个父组件的事件 `enlarge-text`-->
    ></blog-post>
  </div>
</div>
const app = Vue.createApp({
  data:() =>({
    posts: [
      { id: 1, title: 'My journey with Vue' },
      { id: 2, title: 'Blogging with Vue' },
      { id: 3, title: 'Why Vue is so fun' }
  ],
  postFontSize: 1
  })
})

app.component('blog-post', {
  props: ['title'],
  emits: ['enlargeText'], //可选,Vue会自动推导,注意用驼峰命名法
  template: `
    <div class="blog-post">
      <h4>{{ title }}</h4>
      <button @click="$emit('enlargeText')"> <!--这里调用父级 'enlargement' 事件 -->
        Enlarge text
      </button>
    </div>
  `
})

app.mount('#blog-posts-events-demo')

传参:

<blog-post ... @enlarge-text="postFontSize += $event"></blog-post> <!--这里接收一个参数作为 $event -->
<blog-post ... @enlarge-text="onEnlargeText"></blog-post> <!--这里接收所有参数传入 `onEnlargeText` -->
template: `
<button @click="$emit('enlargeText', 0.1)"> <!--这里传入一个参数-->
  Enlarge text
</button>
`

自定义 v-model

<custom-input v-model="searchText" ></custom-input>
<!--等价于-->
<custom-input
  :model-value="searchText"
  @update:model-value="searchText = $event"
></custom-input>
const app = Vue.createApp({data:() =>({searchText:""})})
app.component('custom-input', {
  props: ['modelValue'],
  emits: ['update:modelValue'],
  template: `
    <input
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)"
    >
  `
})

自定义内容 <slot>

<div id="slots-demo" class="demo">
  <alert-box>
    Something bad happened. <!--这些内容会插入<slot></slot>中间-->
  </alert-box>
</div>
const app = Vue.createApp({})

app.component('alert-box', {
  template: `
    <div class="demo-alert-box">
      <strong>Error!</strong>
      <slot></slot>
    </div>
  `
})

app.mount('#slots-demo')

综合 · 动态组件

<div id="dynamic-component-demo" class="demo">
  <button
     v-for="tab in tabs"
     v-bind:key="tab"
     v-bind:class="['tab-button', { active: currentTab === tab }]"
     v-on:click="currentTab = tab"
   >
    {{ tab }}
  </button>
  <!--`:is` 的内容可以是 已注册组件的名字 或是 一个组件选项对象 甚至是 常规的 HTML 元素-->
  <component v-bind:is="currentTabComponent" class="tab"></component>
</div>
const app = Vue.createApp({
  data() {
    return {
      currentTab: 'Home',
      tabs: ['Home', 'Posts', 'Archive']
    }
  },
  computed: {
    currentTabComponent() {
      return 'tab-' + this.currentTab.toLowerCase()
    }
  }
})

app.component('tab-home', {
  template: `<div class="demo-tab">Home component</div>`
})
app.component('tab-posts', {
  template: `<div class="demo-tab">Posts component</div>`
})
app.component('tab-archive', {
  template: `<div class="demo-tab">Archive component</div>`
})

app.mount('#dynamic-component-demo')
posted @   wsm25  阅读(42)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示