vue3笔记

参考:https://www.runoob.com/vue3/vue3-tutorial.html
源地址:https://gitee.com/gaohaixiang192/study-node/blob/master/vue%E7%AC%94%E8%AE%B0-%E7%89%88%E6%9C%AC3.md

vue3 安装

独立版本

  • https://unpkg.com/vue@3.2.36/dist/vue.global.js

npm 安装

  • cnpm install vue@next

命令行工具

  • yarn global add @vue/cli 或 cnpm install -g @vue/cli
  • 版本查看 vue --version
  • 在Vue项目中运行 vue upgrade --next

创建项目并运行项目

  • vue init webpack runoob-vue3-test
  • cd runoob-vue3-test
  • npm run dev

Vite(推荐),创建并运行项目

  • npm init vite-app runoob-vue3-test2
  • cd runoob-vue3-test2
  • npm install
  • npm run dev

vue3 打包

npm run build

vue3 创建项目

vue create 命令

  • vue create [options]
创建一个由 vue-cli-service 提供支持的新项目:
options 选项可以是:
    -p, --preset <presetName>: 忽略提示符并使用已保存的或远程的预设选项
    -d, --default: 忽略提示符并使用默认预设选项
    -i, --inlinePreset <json>: 忽略提示符并使用内联的 JSON 字符串预设选项
    -m, --packageManager <command>: 在安装依赖时使用指定的 npm 客户端
    -r, --registry <url>: 在安装依赖时使用指定的 npm registry
    -g, --git [message]: 强制 / 跳过 git 初始化,并可选的指定初始化提交信息
    -n, --no-git: 跳过 git 初始化
    -f, --force: 覆写目标目录可能存在的配置
    -c, --clone: 使用 git clone 获取远程预设选项
    -x, --proxy: 使用指定的代理创建项目
    -b, --bare: 创建项目时省略默认组件中的新手指导信息
    -h, --help: 输出使用帮助信息
  • vue create runoob-vue3-app
  • cd runoob-vue3-app
  • npm run serve

vue ui 命令

  • vue ui

vue3 目录结构

目录解析
目录/文件	说明
build	        项目构建(webpack)相关代码
config	        配置目录,包括端口号等。我们初学可以使用默认的。
node_modules	npm 加载的项目依赖模块
src	            这里是我们要开发的目录,基本上要做的事情都在这个目录里。里面包含了几个目录及文件:
                assets: 放置一些图片,如logo等。
                components: 目录里面放了一个组件文件,可以不用。
                App.vue: 项目入口文件,我们也可以直接将组件写这里,而不使用 components 目录。
                main.js: 项目的核心文件。
                index.css: 样式文件。

static	        静态资源目录,如图片、字体等。
public	        公共资源目录。
test	        初始测试目录,可删除
.xxxx文件	    这些是一些配置文件,包括语法配置,git配置等。
index.html	    首页入口文件,你可以添加一些 meta 信息或统计代码啥的。
package.json	项目配置文件。
README.md	    项目的说明文档,markdown 格式
dist	        使用 npm run build 命令打包后会生成该目录。

vue3 起步

  • Vue3 中的应用是通过使用 createApp 函数来创建的,语法格式如下:
    const app = Vue.createApp({ /* 选项 */ })
  • 传递给 createApp 的选项用于配置根组件。在使用 mount() 挂载应用时,该组件被用作渲染的起点。示例如下:
    Vue.createApp(HelloVueApp).mount(‘#hello-vue’)
createApp 的参数是根组件(HelloVueApp),在挂载应用时,该组件是渲染的起点。
一个应用需要被挂载到一个 DOM 元素中,
以上代码使用 mount('#hello-vue') 将 Vue 应用 HelloVueApp 挂载到 <div id="hello-vue"></div> 中。
<div id="hello-vue" class="demo">
  {{ message }}
</div>

<script>
const HelloVueApp = {
  data() {
    return {
      message: 'Hello Vue!!'
    }
  }
}

Vue.createApp(HelloVueApp).mount('#hello-vue')
</script>
  • mount(‘#hello-vue’) 将 Vue 应用 HelloVueApp 挂载到
    中。
  • {{ }} 用于输出对象属性和函数返回值。
  • {{ message }} 对应应用中 message 的值。

data 选项

  • data 选项是一个函数。Vue 在创建新组件实例的过程中调用此函数。
    它应该返回一个对象,然后 Vue 会通过响应性系统将其包裹起来,并以 $data 的形式存储在组件实例中。
const app = Vue.createApp({
  data() {
    return { count: 4 }
  }
})

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

document.write(vm.$data.count) // => 4
document.write("<br>")
document.write(vm.count)       // => 4
document.write("<br>")
// 修改 vm.count 的值也会更新 $data.count
vm.count = 5
document.write(vm.$data.count) // => 5
document.write("<br>")
// 反之亦然
vm.$data.count = 6
document.write(vm.count) // => 6
  • 以上实例属性仅在实例首次创建时被添加,所以你需要确保它们都在 data 函数返回的对象中。

方法

  • 我们可以在组件中添加方法,使用 methods 选项,该选项包含了所需方法的对象。
  • 以下实例我们添加了 methods 选项,选项中包含了 increment() 方法:
const app = Vue.createApp({
  data() {
    return { count: 4 }
  },
  methods: {
    increment() {
      // `this` 指向该组件实例
      this.count++
    }
  }
})

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

document.write(vm.count) // => 4
document.write("<br>")
vm.increment()

document.write(vm.count) // => 5

vue3 模板语法

  • Vue 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。
  • Vue 的核心是一个允许你采用简洁的模板语法来声明式的将数据渲染进 DOM 的系统。
  • 结合响应系统,在应用状态改变时, Vue 能够智能地计算出重新渲染组件的最小代价并应用到 DOM 操作上。
插值

文本,文本插值

  • 数据绑定最常见的形式就是使用 {{…}}(双大括号)的文本插值:
<div id="app">
  <p>{{ message }}</p>
</div>
  • {{…}} 标签的内容将会被替代为对应组件实例中 message 属性的值,如果 message 属性的值发生了改变,{{…}} 标签内容也会更新。
  • 如果不想改变标签的内容,可以通过使用 v-once 指令执行一次性地插值,当数据改变时,插值处的内容不会更新。
<span v-once>这个将不会改变: {{ message }}</span>

Html,v-html 指令

  • 使用 v-html 指令用于输出 html 代码:
<div id="example1" class="demo">
    <p>使用双大括号的文本插值: {{ rawHtml }}</p>
    <p>使用 v-html 指令: <span v-html="rawHtml"></span></p>
</div>
 
<script>
const RenderHtmlApp = {
  data() {
    return {
      rawHtml: '<span style="color: #ff0000">这里会显示红色!</span>'
    }
  }
}
 
Vue.createApp(RenderHtmlApp).mount('#example1')
</script>

属性,v-bind 指令

  • HTML 属性中的值应使用 v-bind 指令。
<div v-bind:id="dynamicId"></div>
  • 对于布尔属性,常规值为 true 或 false,如果属性值为 null 或 undefined,则该属性不会显示出来。
<button v-bind:disabled="isButtonDisabled">按钮</button>
  • 以上代码中如果 isButtonDisabled 的值是 null 或 undefined,则 disabled 属性甚至不会被包含在渲染出来的 元素中。
  • 以下实例判断 use 的值,如果为 true 使用 class1 类的样式,否则不使用该类:
// v-bind 指令
<div id="app">
  <label for="r1">修改颜色</label><input type="checkbox" v-model="use" id="r1">
  <br><br>
  <div v-bind:class="{'class1': use}">
    v-bind:class 指令
  </div>
</div>
    
<script>
const app = {
  data() {
    return {
      use: false
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>

表达式

  • Vue.js 都提供了完全的 JavaScript 表达式支持。
// JavaScript 表达式
<div id="app">
    {{5+5}}<br>
    {{ ok ? 'YES' : 'NO' }}<br>
    {{ message.split('').reverse().join('') }}
    <div v-bind:id="'list-' + id">菜鸟教程</div>
</div>
    
<script>
const app = {
  data() {
    return {
      ok: true,
      message: 'RUNOOB!!',
      id: 1
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>
表达式会在当前活动实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能包含单个表达式,所以下面的例子都不会生效:
<!--  这是语句,不是表达式:-->
{{ var a = 1 }}
<!-- 流控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }}
指令
  • 指令是带有 v- 前缀的特殊属性。
  • 指令用于在表达式的值改变时,将某些行为应用到 DOM 上。如下例子:
<div id="app">
    <p v-if="seen">现在你看到我了</p>
</div>
    
<script>
const app = {
  data() {
    return {
      seen: true  /* 改为false,信息就无法显示 */
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>
  • 这里, v-if 指令将根据表达式 seen 的值( true 或 false )来决定是否插入 p 元素。
  • 另外还有其它很多指令,每个都有特殊的功能。例如,v-for 指令可以绑定数组的数据来渲染一个项目列表:
<div id="app">
  <ol>
    <li v-for="site in sites">
      {{ site.text }}
    </li>
  </ol>
</div>
<script>
const app = {
  data() {
    return {
      sites: [
        { text: 'Google' },
        { text: 'Runoob' },
        { text: 'Taobao' }
      ]
    }
  }
}

Vue.createApp(app).mount('#app')
</script>

参数

  • 参数在指令后以冒号指明。例如, v-bind 指令被用来响应地更新 HTML 属性:
<div id="app">
    <p><a v-bind:href="url">菜鸟教程</a></p>
</div>
    
<script>
const app = {
  data() {
    return {
      url: 'https://www.runoob.com'
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>
  • 在这里 href 是参数,告知 v-bind 指令将该元素的 href 属性与表达式 url 的值绑定。
  • 另一个例子是 v-on 指令,它用于监听 DOM 事件:
<!-- 完整语法 -->
<a v-on:click="doSomething"> ... </a>

<!-- 缩写 -->
<a @click="doSomething"> ... </a>

<!-- 动态参数的缩写 (2.6.0+) -->
<a @[event]="doSomething"> ... </a>
  • 在这里参数是监听的事件名。

修饰符

  • 修饰符是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。
    例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault():
<form v-on:submit.prevent="onSubmit"></form>
用户输入
  • 在 input 输入框中我们可以使用 v-model 指令来实现双向数据绑定:
// 双向数据绑定
<div id="app">
    <p>{{ message }}</p>
    <input v-model="message">
</div>
 
<script>
const app = {
  data() {
    return {
      message: 'Runoob!'
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>
  • v-model 指令用来在 input、select、textarea、checkbox、radio 等表单控件元素上创建双向数据绑定,根据表单上的值,自动更新绑定的元素的值。
  • 按钮的事件我们可以使用 v-on 监听事件,并对用户的输入进行响应。
  • 以下实例在用户点击按钮后对字符串进行反转操作:
// 字符串反转
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="app">
    <p>{{ message }}</p>
    <button v-on:click="reverseMessage">反转字符串</button>
</div>
    
<script>
const app = {
  data() {
    return {
      message: 'Runoob!'
    }
  },
  methods: {
    reverseMessage() {
      this.message = this.message
        .split('')
        .reverse()
        .join('')
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>
缩写

v-bind 缩写

  • Vue.js 为两个最为常用的指令提供了特别的缩写:
<!-- 完整语法 -->
<a v-bind:href="url"></a>
<!-- 缩写 -->
<a :href="url"></a>

v-on 缩写

<!-- 完整语法 -->
<a v-on:click="doSomething"></a>
<!-- 缩写 -->
<a @click="doSomething"></a>

vue3 条件语句

条件判断

v-if

  • 条件判断使用 v-if 指令,指令的表达式返回 true 时才会显示:
<div id="app">
    <p v-if="seen">现在你看到我了</p>
</div>
    
<script>
const app = {
  data() {
    return {
      seen: true /* 改为false,信息就无法显示 */
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>

  • 这里, v-if 指令将根据表达式 seen 的值( true 或 false )来决定是否插入 p 元素。
  • 因为 v-if 是一个指令,所以必须将它添加到一个元素上。如果是多个元素,可以包裹在 元素上,并在上面使用 v-if。最终的渲染结果将不包含 元素。
<div id="app">
    <template v-if="seen">
        <h1>网站</h1>
        <p>Google</p>
        <p>Runoob</p>
        <p>Taobao</p>
    </template>
</div>
    
<script>
const app = {
  data() {
    return {
      seen: true /* 改为false,信息就无法显示 */
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>

v-else

  • 可以用 v-else 指令给 v-if 添加一个 “else” 块:
  • 随机生成一个数字,判断是否大于 0.5,然后输出对应信息:
<div id="app">
    <div v-if="Math.random() > 0.5">
      随机数大于 0.5
    </div>
    <div v-else>
      随机数小于等于 0.5
    </div>
</div>
    
<script>
Vue.createApp(app).mount('#app')
</script>

v-else-if

  • v-else-if 即 v-if 的 else-if 块,可以链式的使用多次:
  • 判断 type 变量的值:
<div id="app">
    <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>
</div>
    
<script>
const app = {
  data() {
    return {
      type: "C" 
    }
  }
}
Vue.createApp(app).mount('#app')
</script>
  • v-else 、v-else-if 必须跟在 v-if 或者 v-else-if之后。

v-show

  • 我们也可以使用 v-show 指令来根据条件展示元素:
<h1 v-show="ok">Hello!</h1>

vue3 循环语句

v-for
  • v-for 指令需要以 site in sites 形式的特殊语法, sites 是源数据数组并且 site 是数组元素迭代的别名。
  • v-for 可以绑定数据到数组来渲染一个列表:
// v-for 实例
<div id="app">
  <ol>
    <li v-for="site in sites">
      {{ site.text }}
    </li>
  </ol>
</div>
<script>
const app = {
  data() {
    return {
      sites: [
        { text: 'Google' },
        { text: 'Runoob' },
        { text: 'Taobao' }
      ]
    }
  }
}

Vue.createApp(app).mount('#app')
</script>
  • v-for 还支持一个可选的第二个参数,参数值为当前项的索引:
// index 为列表项的索引值:
<div id="app">
  <ol>
    <li v-for="(site, index) in sites">
      {{ index }} -{{ site.text }}
    </li>
  </ol>
</div>
<script>
const app = {
  data() {
    return {
      sites: [
        { text: 'Google' },
        { text: 'Runoob' },
        { text: 'Taobao' }
      ]
    }
  }
}

Vue.createApp(app).mount('#app')
</script>
  • 模板 中使用 v-for:
<div id="app">
	<ul>
	  <template v-for="site in sites">
		<li>{{ site.text }}</li>
		<li>--------------</li>
	  </template>
	</ul>
</div>
<script>
const app = {
  data() {
    return {
      sites: [
        { text: 'Google' },
        { text: 'Runoob' },
        { text: 'Taobao' }
      ]
    }
  }
}

Vue.createApp(app).mount('#app')
</script>

v-for 迭代对象

  • v-for 可以通过一个对象的属性来迭代数据:
<div id="app">
  <ul>
    <li v-for="value in object">
    {{ value }}
    </li>
  </ul>
</div>
 
<script>
const app = {
  data() {
    return {
      object: {
        name: '菜鸟教程',
        url: 'http://www.runoob.com',
        slogan: '学的不仅是技术,更是梦想!'
      }
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>
  • 你也可以提供第二个的参数为键名:
<div id="app">
  <ul>
    <li v-for="(value, key) in object">
    {{ key }} : {{ value }}
    </li>
  </ul>
</div>
 
<script>
const app = {
  data() {
    return {
      object: {
        name: '菜鸟教程',
        url: 'http://www.runoob.com',
        slogan: '学的不仅是技术,更是梦想!'
      }
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>
  • 第三个参数为索引:
<div id="app">
  <ul>
    <li v-for="(value, key, index) in object">
     {{ index }}. {{ key }} : {{ value }}
    </li>
  </ul>
</div>
 
<script>
const app = {
  data() {
    return {
      object: {
        name: '菜鸟教程',
        url: 'http://www.runoob.com',
        slogan: '学的不仅是技术,更是梦想!'
      }
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>

v-for 迭代整数

<div id="app">
  <ul>
    <li v-for="n in 10">
     {{ n }}
    </li>
  </ul>
</div>
 
<script>
	Vue.createApp(app).mount('#app')
</script>
显示过滤/排序后的结果
  • 我们可以对数组的元素进行处理后再显示出来,一般可以通过创建一个计算属性,来返回过滤或排序后的数组。
// 输出数组中的偶数:
<div id="app">
  <ul>
    <li v-for="n in evenNumbers">{{ n }}</li>
  </ul>
</div>
 
<script>
const app = {
    data() {
        return {
            numbers: [ 1, 2, 3, 4, 5 ]
	     }
    },
    computed: {
        evenNumbers() {
            return this.numbers.filter(number => number % 2 === 0)
        }
    }
}
 
Vue.createApp(app).mount('#app')
</script>

v-for/v-if 联合使用

  • 以上实例联合使用 v-for/v-if 给 select 设置默认值:
  • v-for 循环出列表,v-if 设置选中值:
<div id="app">
   <select @change="changeVal($event)" v-model="selOption">
      <template v-for="(site,index) in sites" :site="site" :index="index" :key="site.id">
         <!-- 索引为 1 的设为默认值,索引值从0 开始-->
         <option v-if = "index == 1" :value="site.name" selected>{{site.name}}</option>
         <option v-else :value="site.name">{{site.name}}</option>
      </template>
   </select>
   <div>您选中了:{{selOption}}</div>
</div>
 
<script>
const app = {
    data() {
        return {
            selOption: "Runoob",
            sites: [
                  {id:1,name:"Google"},
                  {id:2,name:"Runoob"},
                  {id:3,name:"Taobao"},
            ]
         }
        
    },
    methods:{
        changeVal:function(event){
            this.selOption = event.target.value;
            alert("你选中了"+this.selOption);
        }
    }
}
 
Vue.createApp(app).mount('#app')
</script>

在组件上使用 v-for

  • 在自定义组件上,你可以像在任何普通元素上一样使用 v-for:
<my-component v-for="item in items" :key="item.id"></my-component>
  • 然而,任何数据都不会被自动传递到组件里,因为组件有自己独立的作用域。为了把迭代数据传递到组件里,我们要使用 props:
<my-component
  v-for="(item, index) in items"
  :item="item"
  :index="index"
  :key="item.id"
></my-component>
  • 不自动将 item 注入到组件里的原因是,这会使得组件与 v-for 的运作紧密耦合。明确组件数据的来源能够使组件在其他场合重复使用。
<div id="todo-list-example">
  <form v-on:submit.prevent="addNewTodo">
    <label for="new-todo">添加 todo</label>
    <input
      v-model="newTodoText"
      id="new-todo"
      placeholder="例如:明天早上跑步"
    />
    <button>添加</button>
  </form>
  <ul>
    <todo-item
      v-for="(todo, index) in todos"
      :key="todo.id"
      :title="todo.title"
      @remove="todos.splice(index, 1)"
    ></todo-item>
  </ul>
</div>
 
<script>
const app = Vue.createApp({
  data() {
    return {
      newTodoText: '',
      todos: [
        {
          id: 1,
          title: '看电影'
        },
        {
          id: 2,
          title: '吃饭'
        },
        {
          id: 3,
          title: '上 RUNOOB 学习'
        }
      ],
      nextTodoId: 4
    }
  },
  methods: {
    addNewTodo() {
      this.todos.push({
        id: this.nextTodoId++,
        title: this.newTodoText
      })
      this.newTodoText = ''
    }
  }
})

app.component('todo-item', {
  template: `
    <li>
      {{ title }}
      <button @click="$emit('remove')">删除</button>
    </li>
  `,
  props: ['title'],
  emits: ['remove']
})

app.mount('#todo-list-example')
</script>

vue3 组件

  • 组件(Component)是 Vue.js 最强大的功能之一。
  • 组件可以扩展 HTML 元素,封装可重用的代码。
  • 组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树:
  • 每个 Vue 应用都是通过用 createApp 函数创建的,传递给 createApp 的选项用于配置根组件。当我们挂载应用时,该组件被用作渲染的起点。
  • 一个应用需要被挂载到一个 DOM 元素中。
  • 以下实例我们将 Vue 应用挂载到
    ,应该传入 #app:
const RootComponent = { /* 选项 */ }
const app = Vue.createApp(RootComponent)
const vm = app.mount('#app')
  • 注册一个全局组件语法格式如下:
const app = Vue.createApp({...})

app.component('my-component-name', {
  /* ... */
})

  • my-component-name 为组件名,/* … */ 部分为配置选项。注册后,我们可以使用以下方式来调用组件:
<my-component-name></my-component-name>
  • 一个简单的 Vue 组件的实例:全局组件实例
// 注册一个简单的全局组件 runoob,并使用它:
<div id="app">
    <runoob></runoob>
</div>
 
<script>
// 创建一个Vue 应用
const app = Vue.createApp({})
 
// 定义一个名为 runoob的新全局组件
app.component('runoob', {
    template: '<h1>自定义组件!</h1>'
})
 
app.mount('#app')
</script>
  • 接下来我们再注册一个 button-counter 组件,在每次点击后,计数器会加 1:
<div id="app">
    <button-counter></button-counter>
</div>

<script>
// 创建一个Vue 应用
const app = Vue.createApp({})

// 定义一个名为 button-counter 的新全局组件
app.component('button-counter', {
  data() {
    return {
      count: 0
    }
  },
  template: `
    <button @click="count++">
      点了 {{ count }} 次!
    </button>`
})
app.mount('#app')
</script>
  • 注意:template 中 ` 是反引号,不是单单引号 '。

组件的复用

  • 你可以将组件进行任意次数的复用:
<div id="app">
    <button-counter></button-counter>
	<button-counter></button-counter>
	<button-counter></button-counter>
</div>

<script>
// 创建一个Vue 应用
const app = Vue.createApp({})

// 定义一个名为 button-counter 的新全局组件
app.component('button-counter', {
  data() {
    return {
      count: 0
    }
  },
  template: `
    <button @click="count++">
      点了 {{ count }} 次!
    </button>`
})
app.mount('#app')
</script>

全局组件

  • 以上的实例中我们的组件都只是通过 component 全局注册的。
  • 全局注册的组件可以在随后创建的 app 实例模板中使用,也包括根实例组件树中的所有子组件的模板中。
// 注册一个简单的全局组件 runoob,并使用它:
<div id="app">
    <runoob></runoob>
</div>
 
<script>
// 创建一个Vue 应用
const app = Vue.createApp({})
 
// 定义一个名为 runoob 的新全局组件
app.component('runoob', {
    template: '<h1>自定义组件!</h1>'
})
 
app.mount('#app')
</script>

局部组件

  • 全局注册往往是不够理想的。比如,如果你使用一个像 webpack 这样的构建系统,全局注册所有的组件意味着即便你已经不再使用一个组件了,
    它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加。
    在这些情况下,你可以通过一个普通的 JavaScript 对象来定义组件:
const ComponentA = {
  /* ... */
}
const ComponentB = {
  /* ... */
}
const ComponentC = {
  /* ... */
}

然后在 components 选项中定义你想要使用的组件:

const app = Vue.createApp({
  components: {
    'component-a': ComponentA,
    'component-b': ComponentB
  }
})
  • 对于 components 对象中的每个属性来说,其属性名就是自定义元素的名字(component-a、component-b),
    其属性值就是这个组件的选项对象(ComponentA、ComponentB)。
    我们也可以在实例选项中注册局部组件,这样组件只能在这个实例中使用:
// 注册一个简单的局部组件 runoobA,并使用它:
<div id="app">
    <runoob-a></runoob-a>
</div>
<script>
var runoobA = {
  template: '<h1>自定义组件!</h1>'
}
 
const app = Vue.createApp({
  components: {
    'runoob-a': runoobA
  }
})
 
app.mount('#app')
</script>

Prop

  • prop 是子组件用来接受父组件传递过来的数据的一个自定义属性。
  • 父组件的数据需要通过 props 把数据传给子组件,子组件需要显式地用 props 选项声明 “prop”:
<div id="app">
  <site-name title="Google"></site-name>
  <site-name title="Runoob"></site-name>
  <site-name title="Taobao"></site-name>
</div>
 
<script>
const app = Vue.createApp({})
 
app.component('site-name', {
  props: ['title'],
  template: `<h4>{{ title }}</h4>`
})
 
app.mount('#app')
</script>
  • 一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。

动态 Prop

  • 类似于用 v-bind 绑定 HTML 特性到一个表达式,也可以用 v-bind 动态绑定 props 的值到父组件的数据中。
    每当父组件的数据变化时,该变化也会传导给子组件:
<div id="app">
  <site-info
    v-for="site in sites"
    :id="site.id"
    :title="site.title"
  ></site-info>
</div>
 
<script>
const Site = {
  data() {
    return {
      sites: [
        { id: 1, title: 'Google' },
        { id: 2, title: 'Runoob' },
        { id: 3, title: 'Taobao' }
      ]
    }
  }
}
 
const app = Vue.createApp(Site)
 
app.component('site-info', {
  props: ['id','title'],
  template: `<h4>{{ id }} - {{ title }}</h4>`
})
 
app.mount('#app')
</script>

Prop 验证

  • 组件可以为 props 指定验证要求。
  • 为了定制 prop 的验证方式,你可以为 props 中的值提供一个带有验证需求的对象,而不是一个字符串数组。例如:
Vue.component('my-component', {
  props: {
    // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
    propA: Number,
    // 多个可能的类型
    propB: [String, Number],
    // 必填的字符串
    propC: {
      type: String,
      required: true
    },
    // 带有默认值的数字
    propD: {
      type: Number,
      default: 100
    },
    // 带有默认值的对象
    propE: {
      type: Object,
      // 对象或数组默认值必须从一个工厂函数获取
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator: function (value) {
        // 这个值必须匹配下列字符串中的一个
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }
})
  • 当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。
type 可以是下面原生构造器:
    String
    Number
    Boolean
    Array
    Object
    Date
    Function
    Symbol
type 也可以是一个自定义构造器,使用 instanceof 检测。

vue3 计算属性

  • 计算属性关键词: computed。
  • 计算属性在处理一些复杂逻辑时是很有用的。
  • 反转字符串的例子:
<div id="app">
    {{ message.split('').reverse().join('') }}
</div>
    
<script>
const app = {
  data() {
    return {
      message: 'RUNOOB!!'
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>
  • 计算属性的实例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://cdn.staticfile.org/vue/3.0.5/vue.global.js"></script>
</head>
<body>
<div id="app">
  <p>原始字符串: {{ message }}</p>
  <p>计算后反转字符串: {{ reversedMessage }}</p>
</div>
    
<script>
const app = {
  data() {
    return {
      message: 'RUNOOB!!'
    }
  },
  computed: {
    // 计算属性的 getter
    reversedMessage: function () {
      // `this` 指向 vm 实例
      return this.message.split('').reverse().join('')
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>
</body>
</html>
  • 声明了一个计算属性 reversedMessage 。
  • 提供的函数将用作属性 vm.reversedMessage 的 getter 。
  • vm.reversedMessage 依赖于 vm.message,在 vm.message 发生改变时,vm.reversedMessage 也会更新。

computed vs methods

  • 我们可以使用 methods 来替代 computed,效果上两个都是一样的,但是 computed 是基于它的依赖缓存,
    只有相关依赖发生改变时才会重新取值。而使用 methods ,在重新渲染的时候,函数总会重新调用执行。
<div id="app">
  <p>原始字符串: {{ message }}</p>
  <p>使用 computed 计算后反转字符串: {{ reversedMessage }}</p>
  <p>使用 methods 计算后反转字符串: {{ reversedMessage2() }}</p>
</div>
    
<script>
const app = {
  data() {
    return {
      message: 'RUNOOB!!'
    }
  },
  computed: {
    // 计算属性的 getter
    reversedMessage: function () {
      // `this` 指向 vm 实例
      return this.message.split('').reverse().join('')
    }
  },
  methods: {
    reversedMessage2: function () {
      return this.message.split('').reverse().join('')
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>
  • 可以说使用 computed 性能会更好,但是如果你不希望缓存,你可以使用 methods 属性。

computed setter

  • computed 属性默认只有 getter ,不过在需要时你也可以提供一个 setter :
<div id="app">
 
</div>
    
<script>
const app = {
  data() {
    return {
      name: 'Google',
      url: 'http://www.google.com'
    }
  },
  computed: {
    site: {
      // getter
      get: function () {
        return this.name + ' ' + this.url
      },
      // setter
      set: function (newValue) {
        var names = newValue.split(' ')
        this.name = names[0]
        this.url = names[names.length - 1]
      }
    }
  }
}
vm = Vue.createApp(app).mount('#app')
document.write('name: ' + vm.name);
document.write('<br>');
document.write('url: ' + vm.url);
document.write('<br>------ 更新数据 ------<br>');
// 调用 setter, vm.name 和 vm.url 也会被对应更新
vm.site = '菜鸟教程 http://www.runoob.com';
document.write('name: ' + vm.name);
document.write('<br>');
document.write('url: ' + vm.url);
</script>
  • 从实例运行结果看在运行 vm.site = ‘菜鸟教程 http://www.runoob.com’; 时,setter 会被调用, vm.name 和 vm.url 也会被对应更新。

vue3 监听属性

  • Vue3 监听属性 watch,我们可以通过 watch 来响应数据的变化。
  • 通过使用 watch 实现计数器:
<div id = "app">
    <p style = "font-size:25px;">计数器: {{ counter }}</p>
    <button @click = "counter++" style = "font-size:25px;">点我</button>
</div>
    
<script>
const app = {
  data() {
    return {
      counter: 1
    }
  }
}
vm = Vue.createApp(app).mount('#app')
vm.$watch('counter', function(nval, oval) {
    alert('计数器值的变化 :' + oval + ' 变为 ' + nval + '!');
});
</script>
  • 进行千米与米之间的换算:
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id = "app">
    千米 : <input type = "text" v-model = "kilometers">
    米 : <input type = "text" v-model = "meters">
</div>
<p id="info"></p>    
<script>
const app = {
  data() {
    return {
      kilometers : 0,
      meters:0
    }
  },
  watch : {
      kilometers:function(val) {
          this.kilometers = val;
          this.meters = this.kilometers * 1000
      },
      meters : function (val) {
          this.kilometers = val/ 1000;
          this.meters = val;
      }
  }
}
vm = Vue.createApp(app).mount('#app')
vm.$watch('kilometers', function (newValue, oldValue) {
    // 这个回调将在 vm.kilometers 改变后调用
    document.getElementById ("info").innerHTML = "修改前值为: " + oldValue + ",修改后值为: " + newValue;
})
</script>
  • 以上代码中我们创建了两个输入框,data 属性中, kilometers 和 meters 初始值都为 0。
    watch 对象创建了 data 对象的两个监控方法: kilometers 和 meters。
  • 当我们再输入框输入数据时,watch 会实时监听数据变化并改变自身的值。

异步加载中使用 watch

  • 异步数据的加载 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<!-- 因为 AJAX 库和通用工具的生态已经相当丰富,Vue 核心代码没有重复 -->
<!-- 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。 -->
<script src="https://cdn.staticfile.org/axios/0.27.2/axios.min.js"></script>
<script src="https://cdn.staticfile.org/vue/3.2.37/vue.global.min.js"></script>
</head>
<body>
<div id="watch-example">
  <p>
    输入一个问题,以 ? 号结尾输出答案:
    <input v-model="question" />
  </p>
  <p>{{ answer }}</p>
</div>  
<script>
const watchExampleVM = Vue.createApp({
   data() {
      return {
         question: '',
         answer: '每个问题结尾需要输入 ? 号。'
      }
   },
   watch: {
      // 每当问题改变时,此功能将运行,以 ? 号结尾,兼容中英文 ?
      question(newQuestion, oldQuestion) {
         if (newQuestion.indexOf('?') > -1 || newQuestion.indexOf('?') > -1) {
            this.getAnswer()
         }
      }
   },
   methods: {
      getAnswer() {
         this.answer = '加载中...'
         axios
            .get('/try/ajax/json_vuetest.php')
            .then(response => {
            this.answer = response.data.answer
         })
            .catch(error => {
            this.answer = '错误! 无法访问 API。 ' + error
         })
      }
   }
}).mount('#watch-example')
</script>
</body>
</html>

vue3 样式绑定

Vue.js class

  • class 与 style 是 HTML 元素的属性,用于设置元素的样式,我们可以用 v-bind 来设置样式属性。
  • v-bind 在处理 class 和 style 时, 表达式除了可以使用字符串之外,还可以是对象或数组。
  • v-bind:class 可以简写为 :class。

class 属性绑定

  • 我们可以为 v-bind:class 设置一个对象,从而动态的切换 class:
  • 实例中将 isActive 设置为 true 显示了一个绿色的 div 块,如果设置为 false 则不显示:
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://unpkg.com/vue@next"></script>
<style>
.active {
    width: 100px;
    height: 100px;
    background: green;
}
</style>
</head>
<body>
<div id="app">
    <div :class="{ 'active': isActive }"></div>
</div>

<script>
const app = {
    data() {
      return {
         isActive: true
      }
   }
}

Vue.createApp(app).mount('#app')
</script>
  • 以上实例 div class 渲染结果为:
<div class="active"></div>
  • 我们也可以在对象中传入更多属性用来动态切换多个 class 。
  • 此外,:class 指令也可以与普通的 class 属性共存。
// text-danger 类背景颜色覆盖了 active 类的背景色:
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://unpkg.com/vue@next"></script>
<style>
.static {
	width: 100px;
	height: 100px;
}
.active {
	background: green;
}
.text-danger {
	background: red;
}
</style>
</head>
<body>
<div id="app">
    <div class="static" :class="{ 'active': isActive, 'text-danger': hasError }">
    </div>
</div>

<script>
const app = {
    data() {
      return {
         isActive: false,
         hasError: true
      }
   }
}

Vue.createApp(app).mount('#app')
</script>
</body>
</html>
  • 以上实例 div class 渲染结果为:
<div class="static text-danger"></div>
  • 当 isActive 或者 hasError 变化时,class 属性值也将相应地更新。
    例如,如果 active 的值为 true,class 列表将变为 “static active text-danger”。
  • 我们也可以直接绑定数据里的一个对象:
// text-danger 类背景颜色覆盖了 active 类的背景色:
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://unpkg.com/vue@next"></script>
<style>
.static {
	width: 100px;
	height: 100px;
}
.active {
	background: green;
}
.text-danger {
	background: red;
}
</style>
</head>
<body>
<div id="app">
    <div class="static" :class="classObject"></div>
</div>

<script>
const app = {
    data() {
      return {
         classObject: {
            'active': false,
            'text-danger': true
         }
      }
   }
}

Vue.createApp(app).mount('#app')
</script>
</body>
</html>
  • 我们也可以在这里绑定一个返回对象的计算属性。这是一个常用且强大的模式:
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://unpkg.com/vue@next"></script>
<style>
.static {
	width: 100px;
	height: 100px;
}
.active {
	background: green;
}
.text-danger {
	background: red;
}
</style>
</head>
<body>
<div id="app">
    <div class="static" :class="classObject"></div>
</div>

<script>
const app = {
   data() {
      return {
         isActive: true,
         error: null
      }
   },
   computed: {
      classObject() {
         return {
            active: this.isActive && !this.error,
            'text-danger': this.error && this.error.type === 'fatal'
         }
      }
   }
}

Vue.createApp(app).mount('#app')
</script>
</body>
</html>

数组语法

  • 我们可以把一个数组传给 v-bind:class ,实例如下:
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://unpkg.com/vue@next"></script>
<style>
.static {
	width: 100px;
	height: 100px;
}
.active {
	background: green;
}
.text-danger {
	background: red;
}
</style>
</head>
<body>
<div id="app">
    <div class="static" :class="[activeClass, errorClass]"></div>
</div>

<script>
const app = {
    data() {
        return {
            activeClass: 'active',
            errorClass: 'text-danger'
        }
    }
}

Vue.createApp(app).mount('#app')
</script>
</body>
</html>
  • 以上实例 div class 渲染结果为:
<div class="static active text-danger"></div>
  • 我们还可以使用三元表达式来切换列表中的 class :
  • errorClass 是始终存在的,isActive 为 true 时添加 activeClass 类:
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://unpkg.com/vue@next"></script>
<style>
.static {
	width: 100px;
	height: 100px;
}
.active {
	background: green;
}
.text-danger {
	background: red;
}
</style>
</head>
<body>
<div id="app">
    <div class="static" :class="[isActive ? activeClass : '', errorClass]"></div>
</div>

<script>
const app = {
    data() {
        return {
            isActive: false,
			activeClass: 'active',
			errorClass: 'text-danger'
        }
    }
}

Vue.createApp(app).mount('#app')
</script>
</body>
</html>
  • 以上实例 div class 渲染结果为:
<div class="static text-danger"></div>

Vue.js style(内联样式)

  • 我们可以在 v-bind:style 直接设置样式,可以简写为 :style:
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="app">
    <div :style="{ color: activeColor, fontSize: fontSize + 'px' }">菜鸟教程</div>
</div>

<script>
const app = {
    data() {
        return {
            activeColor: 'red',
			fontSize: 30
        }
    }
}

Vue.createApp(app).mount('#app')
</script>
</body>
</html>
  • 以上实例 div style 渲染结果为:
<div style="color: red; font-size: 30px;">菜鸟教程</div>
  • 也可以直接绑定到一个样式对象,让模板更清晰:
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="app">
    <div :style="styleObject">菜鸟教程</div>
</div>

<script>
const app = {
    data() {
        return {
			styleObject: {
                color: "red",
			    fontSize: "30px"
			}
        }
    }
}

Vue.createApp(app).mount('#app')
</script>
</body>
</html>
  • v-bind:style 可以使用数组将多个样式对象应用到一个元素上:
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="app">
    <div :style="[baseStyles, overridingStyles]">菜鸟教程</div>
</div>

<script>
const app = {
    data() {
        return {
			baseStyles: {
                color: 'green',
                fontSize: '30px'
            },
	        overridingStyles: {
                'font-weight': 'bold'
            }
        }
    }
}

Vue.createApp(app).mount('#app')
</script>
</body>
</html>
  • 注意:当 v-bind:style 使用需要特定前缀的 CSS 属性时,如 transform ,Vue.js 会自动侦测并添加相应的前缀。

多重值

  • 可以为 style 绑定中的 property 提供一个包含多个值的数组,常用于提供多个带前缀的值,例如:
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
  • 这样写只会渲染数组中最后一个被浏览器支持的值。在本例中,如果浏览器支持不带浏览器前缀的 flexbox,那么就只会渲染 display: flex。

组件上使用 class 属性

  • 当你在带有单个根元素的自定义组件上使用 class 属性时,这些 class 将被添加到该元素中。此元素上的现有 class 将不会被覆盖。
<div id="app">
    <runoob class="classC classD"></runoob>
</div>
 
<script>
// 创建一个Vue 应用
const app = Vue.createApp({})
 
// 定义一个名为 runoob的新全局组件
app.component('runoob', {
    template: '<h1 class="classA classB">I like runoob!</h1>'
})
 
app.mount('#app')
</script>
  • 以上实例 div class 渲染结果为:
<h1 class="classA classB classC classD">I like runoob!</h1>
  • 对于带数据绑定 class 也同样适用:
<my-component :class="{ active: isActive }"></my-component>
  • 当 isActive 为 true 时,HTML 将被渲染成为:
<p class="active">Hi</p>
  • 如果你的组件有多个根元素,你需要定义哪些部分将接收这个类。可以使用 $attrs 组件属性执行此操作:
<div id="app">
    <runoob class="classA"></runoob>
</div>
 
<script>
const app = Vue.createApp({})
 
app.component('runoob', {
  template: `
    <p :class="$attrs.class">I like runoob!</p>
    <span>这是一个子组件</span>
  `
})
 
app.mount('#app')
</script>
  • 注意:template 中 ` 是反引号,不是单引号 '。
  • 以上实例 div class 渲染结果为:
<div id="app" data-v-app=""><p class="classA">I like runoob!</p><span>这是一个子组件</span></div>

vue3 事件处理

  • 我们可以使用 v-on 指令来监听 DOM 事件,从而执行 JavaScript 代码。

  • v-on 指令可以缩写为 @ 符号。

  • 语法格式:

v-on:click="methodName"
或
@click="methodName"
<div id="app">
  <button @click="counter += 1">增加 1</button>
  <p>这个按钮被点击了 {{ counter }} 次。</p>
</div>
 
<script>
const app = {
  data() {
    return {
      counter: 0
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>
  • 通常情况下,我们需要使用一个方法来调用 JavaScript 方法。
  • v-on 可以接收一个定义的方法来调用。
<div id="app">
  <!-- `greet` 是在下面定义的方法名 -->
  <button @click="greet">点我</button>
</div>
 
<script>
const app = {
  data() {
    return {
      name: 'Runoob'
    }
  },
  methods: {
    greet(event) {
      // `methods` 内部的 `this` 指向当前活动实例
      alert('Hello ' + this.name + '!')
      // `event` 是原生 DOM event
      if (event) {
        alert(event.target.tagName)
      }
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>
  • 除了直接绑定到一个方法,也可以用内联 JavaScript 语句:
<div id="app">
  <button @click="say('hi')">Say hi</button>
  <button @click="say('what')">Say what</button>
</div>
 
<script>
const app = {
  data() {
   
  },
  methods: {
    say(message) {
      alert(message)
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>
  • 事件处理程序中可以有多个方法,这些方法由逗号运算符分隔:
<div id="app">
  <!-- 这两个 one() 和 two() 将执行按钮点击事件 -->
  <button @click="one($event), two($event)">
  点我
  </button>
</div>
 
<script>
const app = {
  data() {
  },
  methods: {
    one(event) {
      alert("第一个事件处理器逻辑...")
    },
    two(event) {
      alert("第二个事件处理器逻辑...")
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>

事件修饰符

  • Vue.js 为 v-on 提供了事件修饰符来处理 DOM 事件细节,
    如:event.preventDefault() 或 event.stopPropagation()。
  • Vue.js 通过由点 . 表示的指令后缀来调用修饰符。
    .stop - 阻止冒泡
    .prevent - 阻止默认事件
    .capture - 阻止捕获
    .self - 只监听触发该元素的事件
    .once - 只触发一次
    .left - 左键事件
    .right - 右键事件
    .middle - 中间滚轮事件

<!-- 阻止单击事件冒泡 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联  -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件侦听器时使用事件捕获模式 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 -->
<div v-on:click.self="doThat">...</div>

<!-- click 事件只能点击一次,2.1.4版本新增 -->
<a v-on:click.once="doThis"></a>

按键修饰符

  • Vue 允许为 v-on 在监听键盘事件时添加按键修饰符:
<!-- 只有在 keyCode 是 13 时调用 vm.submit() -->
<input v-on:keyup.13="submit">
  • 记住所有的 keyCode 比较困难,所以 Vue 为最常用的按键提供了别名:
<!-- 同上 -->
<input v-on:keyup.enter="submit">
<!-- 缩写语法 -->
<input @keyup.enter="submit">
  • 全部的按键别名:
    .enter
    .tab
    .delete (捕获 "删除" 和 "退格" 键)
    .esc
    .space
    .up
    .down
    .left
    .right
  • 系统修饰键:
    .ctrl
    .alt
    .shift
    .meta
  • 鼠标按钮修饰符:
    .left
    .right
    .middle
<p><!-- Alt + C -->
<input @keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>

.exact 修饰符

  • .exact 修饰符允许你控制由精确的系统修饰符组合触发的事件。
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button @click.ctrl="onClick">A</button>

<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>

<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button @click.exact="onClick">A</button>

vue3 表单

  • 我们可以用 v-model 指令在表单 、 及 等元素上创建双向数据绑定。
  • v-model 会根据控件类型自动选取正确的方法来更新元素。
  • v-model 会忽略所有表单元素的 value、checked、selected 属性的初始值,使用的是 data 选项中声明初始值。
  • v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:
    text 和 textarea 元素使用 value 属性和 input 事件;
    checkbox 和 radio 使用 checked 属性和 change 事件;
    select 字段将 value 作为属性并将 change 作为事件。

输入框

  • 实例中演示了 input 和 textarea 元素中使用 v-model 实现双向数据绑定:
<div id="app">
  <p>input 元素:</p>
  <input v-model="message" placeholder="编辑我……">
  <p>input 表单消息是: {{ message }}</p>
    
  <p>textarea 元素:</p>
  <textarea v-model="message2" placeholder="多行文本输入……"></textarea>
  <p>textarea 表单消息是:</p>
  <p style="white-space: pre">{{ message2 }}</p>
  
</div>
 
<script>
const app = {
  data() {
    return {
      message: '',
      message2: '菜鸟教程\r\nhttps://www.runoob.com'
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>
  • 在文本区域 textarea 插值是不起作用,需要使用 v-model 来代替:
<!-- 错误 -->
<textarea>{{ text }}</textarea>

<!-- 正确 -->
<textarea v-model="text"></textarea>

复选框

  • 复选框如果是一个为逻辑值,如果是多个则绑定到同一个数组:
  • 以下实例中演示了复选框的双向数据绑定:
<div id="app">
  <p>单个复选框:</p>
  <input type="checkbox" id="checkbox" v-model="checked">
  <label for="checkbox">{{ checked }}</label>
    
  <p>多个复选框:</p>
  <input type="checkbox" id="runoob" value="Runoob" v-model="checkedNames">
  <label for="runoob">Runoob</label>
  <input type="checkbox" id="google" value="Google" v-model="checkedNames">
  <label for="google">Google</label>
  <input type="checkbox" id="taobao" value="Taobao" v-model="checkedNames">
  <label for="taobao">taobao</label>
  <br>
  <span>选择的值为: {{ checkedNames }}</span>
</div>
 
<script>
const app = {
  data() {
    return {
      checked : false,
      checkedNames: []
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>

单选按钮

  • 以下实例中演示了单选按钮的双向数据绑定:
<div id="app">
  <input type="radio" id="runoob" value="Runoob" v-model="picked">
  <label for="runoob">Runoob</label>
  <br>
  <input type="radio" id="google" value="Google" v-model="picked">
  <label for="google">Google</label>
  <br>
  <span>选中值为: {{ picked }}</span>
</div>
 
<script>
const app = {
  data() {
    return {
      picked : 'Runoob'
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>

select 列表

  • 以下实例中演示了下拉列表的双向数据绑定:
<div id="app">
  <select v-model="selected" name="site">
    <option value="">选择一个网站</option>
    <option value="www.runoob.com">Runoob</option>
    <option value="www.google.com">Google</option>
  </select>
 
  <div id="output">
      选择的网站是: {{selected}}
  </div>
</div>
 
<script>
const app = {
  data() {
    return {
      selected: '' 
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>
  • 多选时会绑定到一个数组:
<div id="app">
  <select v-model="selected" name="fruit" multiple>
    <option value="www.runoob.com">Runoob</option>
    <option value="www.google.com">Google</option>
    <option value="www.taobao.com">Taobao</option>
  </select>
 
  <div id="output">
      选择的网站是: {{selected}}
  </div>
</div>
 
<script>
const app = {
  data() {
    return {
      selected: '' 
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>
  • 使用 v-for 循环输出选项:
<div id="app" class="demo">
  <select v-model="selected">
    <option v-for="option in options" :value="option.value">
      {{ option.text }}
    </option>
  </select>
  <span>选择的是: {{ selected }}</span>
</div>
 
<script>
const app = {
  data() {
    return {
      selected: 'www.runoob.com',
      options: [
        { text: 'Runoob', value: 'www.runoob.com' },
        { text: 'Google', value: 'www.google.com' },
        { text: 'Taobao', value: 'www.taobao.com' }
      ]
    }
  }
}
 
Vue.createApp(app).mount('#app')
</script>

值绑定

  • 对于单选按钮,复选框及选择框的选项,v-model 绑定的值通常是静态字符串 (对于复选框也可以是布尔值):
<!-- 当选中时,`picked` 为字符串 "a" -->
<input type="radio" v-model="picked" value="a" />

<!-- `toggle` 为 true 或 false -->
<input type="checkbox" v-model="toggle" />

<!-- 当选中第一个选项时,`selected` 为字符串 "abc" -->
<select v-model="selected">
  <option value="abc">ABC</option>
</select>
  • 但是有时我们可能想把值绑定到当前活动实例的一个动态属性上,
    这时可以用 v-bind 实现,此外,使用 v-bind 可以将输入值绑定到非字符串。

  • 复选框 (Checkbox):

<input type="checkbox" v-model="toggle" true-value="yes" false-value="no" />
...
// 选中时
vm.toggle === 'yes'
// 取消选中 
vm.toggle === 'no'
  • 单选框 (Radio):
<input type="radio" v-model="pick" v-bind:value="a" />
// 当选中时
vm.pick === vm.a
  • 选择框选项 (Select):
<select v-model="selected">
  <!-- 内联对象字面量 -->
  <option :value="{ number: 123 }">123</option>
</select>
// 当被选中时
typeof vm.selected // => 'object'
vm.selected.number // => 123
修饰符

.lazy

  • 在默认情况下, v-model 在 input 事件中同步输入框的值与数据,
    但你可以添加一个修饰符 lazy ,从而转变为在 change 事件中同步:
<!-- 在 "change" 而不是 "input" 事件中更新 -->
<input v-model.lazy="msg" >

.number

  • 如果想自动将用户的输入值转为 Number 类型(如果原值的转换结果为 NaN 则返回原值),
    可以添加一个修饰符 number 给 v-model 来处理输入值:
<input v-model.number="age" type="number">
  • 这通常很有用,因为在 type=“number” 时 HTML 中输入的值也总是会返回字符串类型。

.trim

  • 如果要自动过滤用户输入的首尾空格,可以添加 trim 修饰符到 v-model 上过滤输入:
<input v-model.trim="msg">

vue3 自定义指令

  • 除了默认设置的核心指令( v-model 和 v-show ), Vue 也允许注册自定义指令。
  • 下面我们注册一个全局指令 v-focus, 该指令的功能是在页面加载时,元素获得焦点:
<div id="app">
    <p>页面载入时,input 元素自动获取焦点:</p>
    <input v-focus>
</div>
 
<script>
const app = Vue.createApp({})
// 注册一个全局自定义指令 `v-focus`
app.directive('focus', {
  // 当被绑定的元素挂载到 DOM 中时……
  mounted(el) {
    // 聚焦元素
    el.focus()
  }
})
app.mount('#app')
</script>
  • 我们也可以在实例使用 directives 选项来注册局部指令,这样指令只能在这个实例中使用:
<div id="app">
    <p>页面载入时,input 元素自动获取焦点:</p>
    <input v-focus>
</div>
 
<script>
const app = {
   data() {
      return {
      }
   },
   directives: {
      focus: {
         // 指令的定义
         mounted(el) {
            el.focus()
         }
      }
   }
}
 
Vue.createApp(app).mount('#app')
钩子

钩子函数

  • 指令定义函数提供了几个钩子函数(可选):
    created : 在绑定元素的属性或事件监听器被应用之前调用。
    beforeMount : 指令第一次绑定到元素并且在挂载父组件之前调用。。
    mounted : 在绑定元素的父组件被挂载后调用。。
    beforeUpdate: 在更新包含组件的 VNode 之前调用。。
    updated: 在包含组件的 VNode 及其子组件的 VNode 更新后调用。
    beforeUnmount: 当指令与在绑定元素父组件卸载之前时,只调用一次。
    unmounted: 当指令与元素解除绑定且父组件已卸载时,只调用一次。
import { createApp } from 'vue'
const app = createApp({})
 
// 注册
app.directive('my-directive', {
  // 指令是具有一组生命周期的钩子:
  // 在绑定元素的 attribute 或事件监听器被应用之前调用
  created() {},
  // 在绑定元素的父组件挂载之前调用
  beforeMount() {},
  // 绑定元素的父组件被挂载时调用
  mounted() {},
  // 在包含组件的 VNode 更新之前调用
  beforeUpdate() {},
  // 在包含组件的 VNode 及其子组件的 VNode 更新之后调用
  updated() {},
  // 在绑定元素的父组件卸载之前调用
  beforeUnmount() {},
  // 卸载绑定元素的父组件时调用
  unmounted() {}
})
 
// 注册 (功能指令)
app.directive('my-directive', () => {
  // 这将被作为 `mounted` 和 `updated` 调用
})
 
// getter, 如果已注册,则返回指令定义
const myDirective = app.directive('my-directive')

钩子函数参数

  • el

  • el 指令绑定到的元素。这可用于直接操作 DOM。

  • binding

  • binding 是一个对象,包含以下属性:

    instance:使用指令的组件实例。
    value:传递给指令的值。例如,在 v-my-directive="1 + 1" 中,该值为 2。
    oldValue:先前的值,仅在 beforeUpdate 和 updated 中可用。值是否已更改都可用。
    arg:参数传递给指令 (如果有)。例如在 v-my-directive:foo 中,arg 为 "foo"。
    modifiers:包含修饰符 (如果有) 的对象。例如在 v-my-directive.foo.bar 中,修饰符对象为 {foo: true,bar: true}。
    dir:一个对象,在注册指令时作为参数传递。例如,在以下指令中:
app.directive('focus', {
  mounted(el) {
    el.focus()
  }
})
  • dir 将会是以下对象:
{
  mounted(el) {
    el.focus()
  }
}
  • vnode

  • 作为 el 参数收到的真实 DOM 元素的蓝图。

  • prevNode

  • 上一个虚拟节点,仅在 beforeUpdate 和 updated 钩子中可用。

  • 以下实例演示了这些参数的使用:

<div id="app">
   <div v-runoob="{ name: '菜鸟教程', url: 'www.runoob.com' }"></div>
</div>
​
<script>
const app = Vue.createApp({})
app.directive('runoob', (el, binding, vnode) => {
console.log(binding.value.name) // => "菜鸟教程"
console.log(binding.value.url) // => "www.runoob.com"
var s = JSON.stringify
el.innerHTML = s(binding.value)
})
app.mount('#app')
</script>
  • 有时候我们不需要其他钩子函数,我们可以简写函数,如下格式:
Vue.directive('runoob', function (el, binding) {
  // 设置指令的背景颜色
  el.style.backgroundColor = binding.value.color
})
  • 指令函数可接受所有合法的 JavaScript 表达式,以下实例传入了 JavaScript 对象:
<div id="app">
    <div v-runoob="{ color: 'green', text: '菜鸟教程!' }"></div>
</div>
 
<script>
Vue.directive('runoob', function (el, binding) {
    // 简写方式设置文本及背景颜色
    el.innerHTML = binding.value.text
    el.style.backgroundColor = binding.value.color
})
new Vue({
  el: '#app'
})
</script>

vue3 路由

  • Vue 路由允许我们通过不同的 URL 访问不同的内容。
  • 通过 Vue 可以实现多视图的单页 Web 应用(single page web application,SPA)。
  • Vue.js 路由需要载入 vue-router 库
  • 中文文档地址:vue-router 文档。
安装
  • 1、直接下载 / CDN
https://unpkg.com/vue-router@4
  • NPM
推荐使用淘宝镜像:
npm install -g cnpm --registry=https://registry.npmmirror.com
cnpm install vue-router@4
实例
  • Vue.js + vue-router 可以很简单的实现单页应用。
  • 是一个组件,该组件用于设置一个导航链接,切换不同 HTML 内容。 to 属性为目标地址, 即要显示的内容。
  • 以下实例中我们将 vue-router 加进来,然后配置组件和路由映射,再告诉 vue-router 在哪里渲染它们。代码如下所示:
// HTML 代码
<script src="https://unpkg.com/vue@3"></script>
<script src="https://unpkg.com/vue-router@4"></script>
 
<div id="app">
  <h1>Hello App!</h1>
  <p>
    <!--使用 router-link 组件进行导航 -->
    <!--通过传递 `to` 来指定链接 -->
    <!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
    <router-link to="/">Go to Home</router-link>
    <router-link to="/about">Go to About</router-link>
  </p>
  <!-- 路由出口 -->
  <!-- 路由匹配到的组件将渲染在这里 -->
  <router-view></router-view>
</div>

router-link

  • 请注意,我们没有使用常规的 a 标签,而是使用一个自定义组件 router-link 来创建链接。
    这使得 Vue Router 可以在不重新加载页面的情况下更改 URL,处理 URL 的生成以及编码。我们将在后面看到如何从这些功能中获益。

router-view

  • router-view 将显示与 url 对应的组件。你可以把它放在任何地方,以适应你的布局。
<div id="app">
  <h1>Hello App!</h1>
  <p>
    <!--使用 router-link 组件进行导航 -->
    <!--通过传递 `to` 来指定链接 -->
    <!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
    <router-link to="/">Go to Home</router-link>
    <router-link to="/about">Go to About</router-link>
  </p>
  <!-- 路由出口 -->
  <!-- 路由匹配到的组件将渲染在这里 -->
  <router-view></router-view>
</div>

<script>
// 1. 定义路由组件.
// 也可以从其他文件导入
const Home = { template: '<div>Home</div>' }
const About = { template: '<div>About</div>' }
 
// 2. 定义一些路由
// 每个路由都需要映射到一个组件。
// 我们后面再讨论嵌套路由。
const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About },
]
 
// 3. 创建路由实例并传递 `routes` 配置
// 你可以在这里输入更多的配置,但我们在这里
// 暂时保持简单
const router = VueRouter.createRouter({
  // 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
  history: VueRouter.createWebHashHistory(),
  routes, // `routes: routes` 的缩写
})
 
// 5. 创建并挂载根实例
const app = Vue.createApp({})
//确保 _use_ 路由实例使
//整个应用支持路由。
app.use(router)
 
app.mount('#app')
 
// 现在,应用已经启动了!
</script>
  • 点击过的导航链接都会加上样式 class =“router-link-exact-active router-link-active”。
相关属性

to

  • 表示目标路由的链接。 当被点击后,内部会立刻把 to 的值传到 router.push(),
    所以这个值可以是一个字符串或者是描述目标位置的对象。
<!-- 字符串 -->
<router-link to="home">Home</router-link>
<!-- 渲染结果 -->
<a href="home">Home</a>

<!-- 使用 v-bind 的 JS 表达式 -->
<router-link v-bind:to="'home'">Home</router-link>

<!-- 不写 v-bind 也可以,就像绑定别的属性一样 -->
<router-link :to="'home'">Home</router-link>

<!-- 同上 -->
<router-link :to="{ path: 'home' }">Home</router-link>

<!-- 命名的路由 -->
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>

<!-- 带查询参数,下面的结果为 /register?plan=private -->
<router-link :to="{ path: 'register', query: { plan: 'private' }}">Register</router-link>

replace

  • 设置 replace 属性的话,当点击时,会调用 router.replace() 而不是 router.push(),导航后不会留下 history 记录。
<router-link :to="{ path: '/abc'}" replace></router-link>

append

  • 设置 append 属性后,则在当前 (相对) 路径前添加其路径。
    例如,我们从 /a 导航到一个相对路径 b,如果没有配置 append,则路径为 /b,如果配了,则为 /a/b
<router-link :to="{ path: 'relative/path'}" append></router-link>

tag

  • 有时候想要 渲染成某种标签,例如

  • 于是我们使用 tag prop 类指定何种标签,同样它还是会监听点击,触发导航。
<router-link to="/foo" tag="li">foo</router-link>
<!-- 渲染结果 -->
<li>foo</li>

active-class

  • 设置 链接激活时使用的 CSS 类名。可以通过以下代码来替代。
<style>
   ._active{
      background-color : red;
   }
</style>
<p>
   <router-link v-bind:to = "{ path: '/route1'}" active-class = "_active">Router Link 1</router-link>
   <router-link v-bind:to = "{ path: '/route2'}" tag = "span">Router Link 2</router-link>
</p>
  • 注意这里 class 使用 active-class=“_active”。

exact-active-class

  • 配置当链接被精确匹配的时候应该激活的 class。可以通过以下代码来替代。
<p>
   <router-link v-bind:to = "{ path: '/route1'}" exact-active-class = "_active">Router Link 1</router-link>
   <router-link v-bind:to = "{ path: '/route2'}" tag = "span">Router Link 2</router-link>
</p>

event

  • 声明可以用来触发导航的事件。可以是一个字符串或是一个包含字符串的数组。
<router-link v-bind:to = "{ path: '/route1'}" event = "mouseover">Router Link 1</router-link>
  • 以上代码设置了 event 为 mouseover ,及在鼠标移动到 Router Link 1 上时导航的 HTML 内容会发生改变。

vue3 混入

  • 混入 (mixins)定义了一部分可复用的方法或者计算属性。混入对象可以包含任意组件选项。
    当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。
<div id = "app"></div>
<script type = "text/javascript">
// 定义混入对象
const myMixin = {
  created() {
    this.hello()
  },
  methods: {
    hello() {
      document.write('欢迎来到混入实例-RUNOOB!')
    }
  }
}
 
// 定义一个应用,使用混入
const app = Vue.createApp({
  mixins: [myMixin]
})
 
app.mount('#app') // => "欢迎来到混入实例-RUNOOB!"
</script>
选项合并
  • 当组件和混入对象含有同名选项时,这些选项将以恰当的方式混合。
  • 比如,数据对象在内部会进行浅合并 (一层属性深度),在和组件的数据发生冲突时以组件数据优先。
  • 以下实例中,Vue 实例与混入对象包含了相同的方法。从输出结果可以看出两个选项合并了。
<div id = "app"></div>
<script type = "text/javascript">
const myMixin = {
  data() {
    return {
      message: 'hello',
      foo: 'runoob'
    }
  }
}
 
const app = Vue.createApp({
  mixins: [myMixin],
  data() {
    return {
      message: 'goodbye',
      bar: 'def'
    }
  },
  created() {
    document.write(JSON.stringify(this.$data)) 
  }
})
 
app.mount('#app')
</script>
  • 同名钩子函数将合并为一个数组,因此都将被调用。另外,mixin 对象的钩子将在组件自身钩子之前调用。
const myMixin = {
  created() {
    console.log('mixin 对象的钩子被调用')
  }
}

const app = Vue.createApp({
  mixins: [myMixin],
  created() {
    console.log('组件钩子被调用')
  }
})

// => "mixin 对象的钩子被调用"
// => "组件钩子被调用"
  • 值为对象的选项,例如 methods、components 和 directives,将被合并为同一个对象。
    两个对象键名冲突时,取组件对象的键值对。
const myMixin = {
  methods: {
    foo() {
      console.log('foo')
    },
    conflicting() {
      console.log('from mixin')
    }
  }
}
 
const app = Vue.createApp({
  mixins: [myMixin],
  methods: {
    bar() {
      console.log('bar')
    },
    conflicting() {
      console.log('from self')
    }
  }
})
 
const vm = app.mount('#app')
 
vm.foo() // => "foo"
vm.bar() // => "bar"
vm.conflicting() // => "from self"
  • 以上实例,我们调用了以下三个方法:
vm.foo();
vm.bar();
vm.conflicting();
  • 从输出结果 methods 选项中如果碰到相同的函数名则 Vue 实例有更高的优先级会执行输出。
全局混入
  • 也可以全局注册混入对象。注意使用! 一旦使用全局混入对象,将会影响到 所有 之后创建的 Vue 实例。
    使用恰当时,可以为自定义对象注入处理逻辑。
<div id = "app"></div>
<script type = "text/javascript">
const app = Vue.createApp({
  myOption: 'hello!'
})
 
// 为自定义的选项 'myOption' 注入一个处理器。
app.mixin({
  created() {
    const myOption = this.$options.myOption
    if (myOption) {
      document.write(myOption)
    }
  }
})
 
app.mount('#app') // => "hello!"
</script>
  • 谨慎使用全局混入对象,因为会影响到每个单独创建的 Vue 实例 (包括第三方模板)。

vue3 Ajax(axios)

  • Vue 版本推荐使用 axios 来完成 ajax 请求。
  • Axios 是一个基于 Promise 的 HTTP 库,可以用在浏览器和 node.js 中。
  • Github开源地址: https://github.com/axios/axios

安装方法

  • 使用 cdn:
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
或
<script src="https://cdn.staticfile.org/axios/0.18.0/axios.min.js"></script>
  • 使用 npm:
$ npm install axios
  • 使用 bower:
$ bower install axios
  • 使用 yarn:
$ yarn add axios

使用方法:

Vue.axios.get(api).then((response) => {
  console.log(response.data)
})

this.axios.get(api).then((response) => {
  console.log(response.data)
})

this.$http.get(api).then((response) => {
  console.log(response.data)
})

GET 方法
  • 我们可以简单的读取 JSON 数据:
// GET 实例
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://unpkg.com/vue@next"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
  {{ info }}
</div>

<script>
const app = {
  data() {
    return {
      info: 'Ajax 测试!!'
    }
  },
  mounted () {
    axios
      .get('http://www.runoob.com/try/ajax/json_demo.json')
      .then(response => (this.info = response))
      .catch(function (error) { // 请求失败处理
        console.log(error);
    });
  }
}

Vue.createApp(app).mount('#app')
</script>
</body>
</html>
  • 使用 response.data 读取 JSON 数据:
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://unpkg.com/vue@next"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
  <h1>网站列表</h1>
  <div
    v-for="site in info"
  >
    {{ site.name }}
  </div>
</div>

<script>
const app = {
  data() {
    return {
      info: 'Ajax 测试!!'
    }
  },
  mounted () {
    axios
      .get('http://www.runoob.com/try/ajax/json_demo.json')
      .then(response => (this.info = response.data.sites))
      .catch(function (error) { // 请求失败处理
        console.log(error);
      });
  }
}

Vue.createApp(app).mount('#app')
</script>
</body>
</html>
  • GET 方法传递参数格式如下:
// 传递参数说明
// 直接在 URL 上添加参数 ID=12345
axios.get('/user?ID=12345')
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });
 
// 也可以通过 params 设置参数:
axios.get('/user', {
    params: {
      ID: 12345
    }
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });
POST 方法
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://unpkg.com/vue@next"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
  {{ info }}
</div>


<script>
const app = {
  data() {
    return {
      info: null
    }
  },
  mounted () {
    axios
      .post('http://www.runoob.com/try/ajax/demo_axios_post.php')
      .then(response => (this.info = response))
      .catch(function (error) { // 请求失败处理
        console.log(error);
      });
  }
}

Vue.createApp(app).mount('#app')
</script>
</body>
</html>

POST 方法传递参数格式如下:

// 传递参数说明
axios.post('/user', {
    firstName: 'Fred',        // 参数 firstName
    lastName: 'Flintstone'    // 参数 lastName
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

执行多个并发请求

function getUserAccount() {
  return axios.get('/user/12345');
}
 
function getUserPermissions() {
  return axios.get('/user/12345/permissions');
}
axios.all([getUserAccount(), getUserPermissions()])
  .then(axios.spread(function (acct, perms) {
    // 两个请求现在都执行完成
  }));

axios API

  • 可以通过向 axios 传递相关配置来创建请求。
axios(config)
// 发送 POST 请求
axios({
  method: 'post',
  url: '/user/12345',
  data: {
    firstName: 'Fred',
    lastName: 'Flintstone'
  }
});
//  GET 请求远程图片
axios({
  method:'get',
  url:'http://bit.ly/2mTM3nY',
  responseType:'stream'
})
  .then(function(response) {
  response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
});
axios(url[, config])
// 发送 GET 请求(默认的方法)
axios('/user/12345');

请求方法的别名

  • 为方便使用,官方为所有支持的请求方法提供了别名,可以直接使用别名来发起请求:
axios.request(config)
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.post(url[, data[, config]])
axios.put(url[, data[, config]])
axios.patch(url[, data[, config]])
  • 注意:在使用别名方法时, url、method、data 这些属性都不必在配置中指定。

并发

  • 处理并发请求的助手函数:
axios.all(iterable)
axios.spread(callback)

创建实例

  • 可以使用自定义配置新建一个 axios 实例:
axios.create([config])
const instance = axios.create({
  baseURL: 'https://some-domain.com/api/',
  timeout: 1000,
  headers: {'X-Custom-Header': 'foobar'}
});

实例方法

  • 以下是可用的实例方法。指定的配置将与实例的配置合并:
axios#request(config)
axios#get(url[, config])
axios#delete(url[, config])
axios#head(url[, config])
axios#post(url[, data[, config]])
axios#put(url[, data[, config]])
axios#patch(url[, data[, config]])

请求配置项

  • 下面是创建请求时可用的配置选项,注意只有 url 是必需的。如果没有指定 method,请求将默认使用 get 方法。
{
  // `url` 是用于请求的服务器 URL
  url: "/user",

  // `method` 是创建请求时使用的方法
  method: "get", // 默认是 get

  // `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
  // 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
  baseURL: "https://some-domain.com/api/",

  // `transformRequest` 允许在向服务器发送前,修改请求数据
  // 只能用在 "PUT", "POST" 和 "PATCH" 这几个请求方法
  // 后面数组中的函数必须返回一个字符串,或 ArrayBuffer,或 Stream
  transformRequest: [function (data) {
    // 对 data 进行任意转换处理

    return data;
  }],

  // `transformResponse` 在传递给 then/catch 前,允许修改响应数据
  transformResponse: [function (data) {
    // 对 data 进行任意转换处理

    return data;
  }],

  // `headers` 是即将被发送的自定义请求头
  headers: {"X-Requested-With": "XMLHttpRequest"},

  // `params` 是即将与请求一起发送的 URL 参数
  // 必须是一个无格式对象(plain object)或 URLSearchParams 对象
  params: {
    ID: 12345
  },

  // `paramsSerializer` 是一个负责 `params` 序列化的函数
  // (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
  paramsSerializer: function(params) {
    return Qs.stringify(params, {arrayFormat: "brackets"})
  },

  // `data` 是作为请求主体被发送的数据
  // 只适用于这些请求方法 "PUT", "POST", 和 "PATCH"
  // 在没有设置 `transformRequest` 时,必须是以下类型之一:
  // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
  // - 浏览器专属:FormData, File, Blob
  // - Node 专属: Stream
  data: {
    firstName: "Fred"
  },

  // `timeout` 指定请求超时的毫秒数(0 表示无超时时间)
  // 如果请求花费了超过 `timeout` 的时间,请求将被中断
  timeout: 1000,

  // `withCredentials` 表示跨域请求时是否需要使用凭证
  withCredentials: false, // 默认的

  // `adapter` 允许自定义处理请求,以使测试更轻松
  // 返回一个 promise 并应用一个有效的响应 (查阅 [response docs](#response-api)).
  adapter: function (config) {
    /* ... */
  },

  // `auth` 表示应该使用 HTTP 基础验证,并提供凭据
  // 这将设置一个 `Authorization` 头,覆写掉现有的任意使用 `headers` 设置的自定义 `Authorization`头
  auth: {
    username: "janedoe",
    password: "s00pers3cret"
  },

  // `responseType` 表示服务器响应的数据类型,可以是 "arraybuffer", "blob", "document", "json", "text", "stream"
  responseType: "json", // 默认的

  // `xsrfCookieName` 是用作 xsrf token 的值的cookie的名称
  xsrfCookieName: "XSRF-TOKEN", // default

  // `xsrfHeaderName` 是承载 xsrf token 的值的 HTTP 头的名称
  xsrfHeaderName: "X-XSRF-TOKEN", // 默认的

  // `onUploadProgress` 允许为上传处理进度事件
  onUploadProgress: function (progressEvent) {
    // 对原生进度事件的处理
  },

  // `onDownloadProgress` 允许为下载处理进度事件
  onDownloadProgress: function (progressEvent) {
    // 对原生进度事件的处理
  },

  // `maxContentLength` 定义允许的响应内容的最大尺寸
  maxContentLength: 2000,

  // `validateStatus` 定义对于给定的HTTP 响应状态码是 resolve 或 reject  promise 。如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),promise 将被 resolve; 否则,promise 将被 rejecte
  validateStatus: function (status) {
    return status &gt;= 200 &amp;&amp; status &lt; 300; // 默认的
  },

  // `maxRedirects` 定义在 node.js 中 follow 的最大重定向数目
  // 如果设置为0,将不会 follow 任何重定向
  maxRedirects: 5, // 默认的

  // `httpAgent` 和 `httpsAgent` 分别在 node.js 中用于定义在执行 http 和 https 时使用的自定义代理。允许像这样配置选项:
  // `keepAlive` 默认没有启用
  httpAgent: new http.Agent({ keepAlive: true }),
  httpsAgent: new https.Agent({ keepAlive: true }),

  // "proxy" 定义代理服务器的主机名称和端口
  // `auth` 表示 HTTP 基础验证应当用于连接代理,并提供凭据
  // 这将会设置一个 `Proxy-Authorization` 头,覆写掉已有的通过使用 `header` 设置的自定义 `Proxy-Authorization` 头。
  proxy: {
    host: "127.0.0.1",
    port: 9000,
    auth: : {
      username: "mikeymike",
      password: "rapunz3l"
    }
  },

  // `cancelToken` 指定用于取消请求的 cancel token
  // (查看后面的 Cancellation 这节了解更多)
  cancelToken: new CancelToken(function (cancel) {
  })
}

响应结构

  • axios请求的响应包含以下信息:
{
  // `data` 由服务器提供的响应
  data: {},

  // `status`  HTTP 状态码
  status: 200,

  // `statusText` 来自服务器响应的 HTTP 状态信息
  statusText: "OK",

  // `headers` 服务器响应的头
  headers: {},

  // `config` 是为请求提供的配置信息
  config: {}
}
  • 使用 then 时,会接收下面这样的响应:
axios.get("/user/12345")
  .then(function(response) {
    console.log(response.data);
    console.log(response.status);
    console.log(response.statusText);
    console.log(response.headers);
    console.log(response.config);
  });
  • 在使用 catch 时,或传递 rejection callback 作为 then 的第二个参数时,响应可以通过 error 对象可被使用。

配置的默认值

  • 你可以指定将被用在各个请求的配置默认值。
  • 全局的 axios 默认值:
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

自定义实例默认值:

// 创建实例时设置配置的默认值
var instance = axios.create({
  baseURL: 'https://api.example.com'
});

// 在实例已创建后修改默认值
instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;

配置的优先顺序

  • 配置会以一个优先顺序进行合并。这个顺序是:在 lib/defaults.js 找到的库的默认值,
    然后是实例的 defaults 属性,最后是请求的 config 参数。后者将优先于前者。这里是一个例子:
// 使用由库提供的配置的默认值来创建实例
// 此时超时配置的默认值是 `0`
var instance = axios.create();

// 覆写库的超时默认值
// 现在,在超时前,所有请求都会等待 2.5 秒
instance.defaults.timeout = 2500;

// 为已知需要花费很长时间的请求覆写超时设置
instance.get('/longRequest', {
  timeout: 5000
});

拦截器

  • 在请求或响应被 then 或 catch 处理前拦截它们。
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  });

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    // 对响应数据做点什么
    return response;
  }, function (error) {
    // 对响应错误做点什么
    return Promise.reject(error);
  });
  • 如果你想在稍后移除拦截器,可以这样:
var myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);
  • 可以为自定义 axios 实例添加拦截器。
var instance = axios.create();
instance.interceptors.request.use(function () {/*...*/});
  • 错误处理:
axios.get('/user/12345')
  .catch(function (error) {
    if (error.response) {
      // 请求已发出,但服务器响应的状态码不在 2xx 范围内
      console.log(error.response.data);
      console.log(error.response.status);
      console.log(error.response.headers);
    } else {
      // Something happened in setting up the request that triggered an Error
      console.log('Error', error.message);
    }
    console.log(error.config);
  });
  • 可以使用 validateStatus 配置选项定义一个自定义 HTTP 状态码的错误范围。
axios.get('/user/12345', {
  validateStatus: function (status) {
    return status < 500; // 状态码在大于或等于500时才会 reject
  }
})

取消

  • 使用 cancel token 取消请求。
  • Axios 的 cancel token API 基于cancelable promises proposal
  • 可以使用 CancelToken.source 工厂方法创建 cancel token,像这样:
var CancelToken = axios.CancelToken;
var source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token
}).catch(function(thrown) {
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
    // 处理错误
  }
});

// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');
  • 还可以通过传递一个 executor 函数到 CancelToken 的构造函数来创建 cancel token:
var CancelToken = axios.CancelToken;
var cancel;

axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    // executor 函数接收一个 cancel 函数作为参数
    cancel = c;
  })
});
// 取消请求
cancel();
  • 注意:可以使用同一个 cancel token 取消多个请求。

请求时使用 application/x-www-form-urlencoded

  • axios 会默认序列化 JavaScript 对象为 JSON。
    如果想使用 application/x-www-form-urlencoded 格式,你可以使用下面的配置。
  • 浏览器
  • 在浏览器环境,你可以使用 URLSearchParams API:
const params = new URLSearchParams();
params.append('param1', 'value1');
params.append('param2', 'value2');
axios.post('/foo', params);
  • URLSearchParams 不是所有的浏览器均支持。
  • 除此之外,你可以使用 qs 库来编码数据:
const qs = require('qs');
axios.post('/foo', qs.stringify({ 'bar': 123 }));

// Or in another way (ES6),

import qs from 'qs';
const data = { 'bar': 123 };
const options = {
  method: 'POST',
  headers: { 'content-type': 'application/x-www-form-urlencoded' },
  data: qs.stringify(data),
  url,
};
axios(options);

Node.js 环境

  • 在 node.js里, 可以使用 querystring 模块:
const querystring = require('querystring');
axios.post('http://something.com/', querystring.stringify({ foo: 'bar' }));
  • 当然,同浏览器一样,你还可以使用 qs 库。

Promises

  • axios 依赖原生的 ES6 Promise 实现而被支持。
  • 如果你的环境不支持 ES6 Promise,你可以使用 polyfill。

TypeScript支持

  • axios 包含 TypeScript 的定义。
import axios from "axios";
axios.get("/user?ID=12345");

vue3 组合式API

  • Vue3 组合式 API(Composition API) 主要用于在大型组件中提高代码逻辑的可复用性。
  • 传统的组件随着业务复杂度越来越高,代码量会不断的加大,整个代码逻辑都不易阅读和理解。
  • Vue3 使用组合式 API 的地方为 setup。
  • 在 setup 中,我们可以按逻辑关注点对部分代码进行分组,然后提取逻辑片段并与其他组件共享代码。
    因此,组合式 API(Composition API) 允许我们编写更有条理的代码。
setup 组件
  • setup() 函数在组件创建 created() 之前执行。
  • setup() 函数接收两个参数 props 和 context。
  • 第一个参数 props,它是响应式的,当传入新的 prop 时,它将被更新。
  • 第二个参数 context 是一个普通的 JavaScript 对象,它是一个上下文对象,暴露了其它可能在 setup 中有用的值。
  • 注意:在 setup 中你应该避免使用 this,因为它不会找到组件实例。
    setup 的调用发生在 data property、computed property 或 methods 被解析之前,所以它们无法在 setup 中被获取。
  • 以下实例使用组合 API 定义一个计数器:
// 实例(src/APP.vue)
<template>
    <div>
        <p>计数器实例: {{ count }}</p>
        <input @click="myFn" type="button" value="点我加 1">
    </div>
</template>

<script>
import {ref, onMounted} from 'vue';

export default {
    setup(){
        //定义初始值为0的变量,要使用ref方法赋值,直接赋值的话变量改变不会更新 UI
        let count = ref(0);

        // 定义点击事件 myFn
        function myFn(){
            console.log(count);
            count.value += 1;
        }
       
       // 组件被挂载时,我们用 onMounted 钩子记录一些消息
        onMounted(() => console.log('component mounted!'));

        // 外部使用组合API中定义的变量或方法,在模板中可用。
        return {count,myFn} // 返回的函数与方法的行为相同
    }
}
</script>
  • 在 Vue 3.0 中,我们可以通过一个新的 ref 函数使任何响应式变量在任何地方起作用,如下所示:
import { ref } from 'vue'
let count = ref(0);
  • ref() 函数可以根据给定的值来创建一个响应式的数据对象,返回值是一个对象,且只包含一个 .value 属性。
  • 在 setup() 函数内,由 ref() 创建的响应式数据返回的是对象,所以需要用 .value 来访问。
import { ref } from 'vue'

const counter = ref(0)

console.log(counter) // { value: 0 }
console.log(counter.value) // 0

counter.value++
console.log(counter.value) // 1
Vue 组合式 API 生命周期钩子
  • 在 Vue2 中,我们通过以下方式实现生命周期钩子函数:
export default {
  beforeMount() {
    console.log('V2 beforeMount!')
  },
  mounted() {
    console.log('V2 mounted!')
  }
};
  • 在 Vue3 组合 API 中实现生命周期钩子函数可以在 setup() 函数中使用带有 on 前缀的函数:
import { onBeforeMount, onMounted } from 'vue';
export default {
  setup() {
    onBeforeMount(() => {
      console.log('V3 beforeMount!');
    })
    onMounted(() => {
      console.log('V3 mounted!');
    })
  }
};
  • 下表为 Options API 和 Composition API 之间的映射,包含如何在 setup () 内部调用生命周期钩子:
Vue2 Options-based API	        Vue Composition API
beforeCreate	                setup()
created	                        setup()
beforeMount	                    onBeforeMount
mounted	                        onMounted
beforeUpdate	                onBeforeUpdate
updated	                        onUpdated
beforeDestroy	                onBeforeUnmount
destroyed	                    onUnmounted
errorCaptured	                onErrorCaptured
  • 因为 setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,
    所以不需要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。

  • 这些函数接受一个回调函数,当钩子被组件调用时将会被执行:

setup() {
...
    // 组件被挂载时,我们用 onMounted 钩子记录一些消息
    onMounted(() => console.log('component mounted!'));
...
}
模板引用
  • 在使用组合式 API 时,响应式引用和模板引用的概念是统一的。
  • 为了获得对模板内元素或组件实例的引用,我们可以像往常一样声明 ref 并从 setup() 返回:
<template>
  <div ref="root">This is a root element</div>
</template>

<script>
  import { ref, onMounted } from 'vue'

  export default {
    setup() {
      const root = ref(null)

      onMounted(() => {
        // DOM 元素将在初始渲染后分配给 ref
        console.log(root.value) // <div>This is a root element</div>
      })

      return {
        root
      }
    }
  }
</script>
  • 以上实例中我们在渲染上下文中暴露 root,并通过 ref=“root”,将其绑定到 div 作为其 ref。
  • 作为模板使用的 ref 的行为与任何其他 ref 一样:它们是响应式的,可以传递到 (或从中返回) 复合函数中。

v-for 中的用法

  • 组合式 API 模板引用在 v-for 内部使用时没有特殊处理。相反,请使用函数引用执行自定义处理:
<template>
  <div v-for="(item, i) in list" :ref="el => { if (el) divs[i] = el }">
    {{ item }}
  </div>
</template>

<script>
  import { ref, reactive, onBeforeUpdate } from 'vue'

  export default {
    setup() {
      const list = reactive([1, 2, 3])
      const divs = ref([])

      // 确保在每次更新之前重置ref
      onBeforeUpdate(() => {
        divs.value = []
      })

      return {
        list,
        divs
      }
    }
  }
</script>

侦听模板引用

  • 侦听模板引用的变更可以替代前面例子中演示使用的生命周期钩子。
  • 但与生命周期钩子的一个关键区别是,watch() 和 watchEffect() 在 DOM 挂载或更新之前运行会有副作用,
    所以当侦听器运行时,模板引用还未被更新。
<template>
  <div ref="root">This is a root element</div>
</template>

<script>
  import { ref, watchEffect } from 'vue'

  export default {
    setup() {
      const root = ref(null)

      watchEffect(() => {
        // 这个副作用在 DOM 更新之前运行,因此,模板引用还没有持有对元素的引用。
        console.log(root.value) // => null
      })

      return {
        root
      }
    }
  }
</script>
  • 因此,使用模板引用的侦听器应该用 flush: ‘post’ 选项来定义,
    这将在 DOM 更新后运行副作用,确保模板引用与 DOM 保持同步,并引用正确的元素。
<template>
  <div ref="root">This is a root element</div>
</template>

<script>
  import { ref, watchEffect } from 'vue'

  export default {
    setup() {
      const root = ref(null)

      watchEffect(() => {
        console.log(root.value) // => <div>This is a root element</div>
      },
      {
        flush: 'post'
      })

      return {
        root
      }
    }
  }
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值