参考:https://www.runoob.com/vue3/vue3-tutorial.html
一、目录结构
目录/文件 | 说明 |
---|---|
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 命令打包后会生成该目录。 |
二、基础
将页面挂载到标签上:Vue.createApp(HelloVueApp).mount(‘#hello-vue’)
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
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
数据绑定
数据绑定最常见的形式就是使用 {{…}}(双大括号)的文本插值:
{{…}} 标签的内容将会被替代为对应组件实例中 message 属性的值,如果 message 属性的值发生了改变,{{…}} 标签内容也会更新。
如果不想改变标签的内容,可以通过使用 v-once 指令执行一次性地插值,当数据改变时,插值处的内容不会更新。
<div id="app"> <p>{{ message }}</p></div>
<span v-once>这个将不会改变: {{ message }}</span>
标签属性
HTML 属性中的值应使用 v-bind 指令。
<div v-bind:id="dynamicId"></div>
对于布尔属性,常规值为 true 或 false,如果属性值为 null 或 undefined,则该属性不会显示出来。
<button v-bind:disabled="isButtonDisabled">按钮</button>
表达式
包裹在 {{…}}(双大括号) 中,可是使用完全的 JavaScript 表达式支持。每个绑定都只能包含单个表达式。
参数
参数在指令后以冒号指明。例如, v-bind 指令被用来响应地更新 HTML 属性:
<div id="app">
<p><a v-bind:href="url">菜鸟教程</a></p>
</div>
修饰符
修饰符是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault():
<form v-on:submit.prevent="onSubmit"></form>
用户输入
在 input 输入框中我们可以使用 v-model 指令来实现双向数据绑定:
三、指令
指令是带有 v- 前缀的特殊属性。指令用于在表达式的值改变时,将某些行为应用到 DOM 上。
已有指令列表
- v-bind(缩写:“:”) :被用来响应地更新 HTML 属性
- v-on(缩写:“@”):对于触发的事件调用 event.preventDefault():
- v-model :实现双向数据绑定
- v-if :条件判断使用 v-if 指令,指令的表达式返回 true 时才会显示
- v-else:配合v-if 使用
- v-else-if:配合v-if 使用
- v-show:根据条件展示元素
- v-for :需要以 site in sites 形式的特殊语法, sites 是源数据数组并且 site 是数组元素迭代的别名
①<li v-for="site in sites">{{ site.text }} </li>(值) <<<<<<<<<>>>>>>>>> ②<li v-for="(site, index) in sites">{{ index }} -{{ site.text }}</li>(值-索引) <<<<<<<<<>>>>>>>>> ③<li v-for="value in object">{{ value }}</li>(值) <<<<<<<<<>>>>>>>>>④ <li v-for="(value, key) in object">{{ key }} : {{ value }}</li>(值-键) <<<<<<<<<>>>>>>>>>⑤ <li v-for="(value, key, index) in object">{{ index }}. {{ key }} : {{ value }}</li>(值-键-索引) <<<<<<<<<>>>>>>>>>⑥<li v-for="n in 10">{{ n }}</li>(值)
自定义指令
除了默认设置的核心指令( v-model 和 v-show ), Vue 也允许注册自定义指令。
注册全局指令
<input v-focus>//使用
// 注册一个全局自定义指令 `v-focus`
app.directive('focus', {
// 指令是具有一组生命周期的钩子:
// 在绑定元素的 attribute 或事件监听器被应用之前调用
created() {},
// 在绑定元素的父组件挂载之前调用
beforeMount() {},
// 绑定元素的父组件被挂载时调用
mounted() {},
// 在包含组件的 VNode 更新之前调用
beforeUpdate() {},
// 在包含组件的 VNode 及其子组件的 VNode 更新之后调用
updated() {},
// 在绑定元素的父组件卸载之前调用
beforeUnmount() {},
// 卸载绑定元素的父组件时调用
unmounted() {}
})
或简写
// 注册 (功能指令)
app.directive('my-directive', () => {
// 这将被作为 `mounted` 和 `updated` 调用
})
注册局部指令
<input v-focus>//使用
const app = {
data() {
return {
}
},
directives: {
focus: {
// 指令的定义
mounted(el) {
el.focus()
}
}
}
}
生命周期函数(钩子)参数
el:指令绑定到的元素。这可用于直接操作 DOM。类型是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:一个对象,在注册指令时作为参数传递。例如,在以下指令中:
vnode:作为 el 参数收到的真实 DOM 元素的蓝图。
prevNode:上一个虚拟节点,仅在 beforeUpdate 和 updated 钩子中可用。
四、组件
组件结构
<!-- 标签显示区域 -->
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<van-button type="primary">主要按钮</van-button>
<van-button type="warning">警告按钮</van-button>
</div>
</template>
<script>
//组件实例区域
export default {
//组件名称
name: 'HelloWorld',
//子组件用来接受父组件传递过来的数据的一个自定义属性。父组件的数据需要通过 props 把数据传给子组件,子组件需要显式地用 props 选项声明
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
}
}
},
//组合式api
//setup() 函数在组件创建 created() 之前执行。
//setup() 函数接收两个参数 props 和 context。
// 第一个参数 props,它是响应式的,当传入新的 prop 时,它将被更新。
// 第二个参数 context 是一个普通的 JavaScript 对象,它是一个上下文对象,暴露了其它可能在 setup 中有用的值。
//注意:在 setup 中你应该避免使用 this,因为它不会找到组件实例。setup 的调用发生在 data property、computed property 或 methods 被解析之前,所以它们无法在 setup 中被获取。
setup() {
return <van-button type="success">成功按钮</van-button>;
},
// 无缓存函数
methods: {
reversedMessage2: function () {
return this.message.split('').reverse().join('')
}
},
//有缓存函数,默认只有getter,可以单独添加setter,基于它的依赖缓存,只有相关依赖发生改变时才会重新取值。
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]
}
}
},
//局部注册自定义指令,只在当前使用
directives: {
focus: {
// 指令的定义
mounted(el) {
el.focus()
}
}
}
</script>
<!-- 标签内容样式 -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
组件注册
全局注册
const app = Vue.createApp({})
app.component('runoob', {template: '<h1>自定义组件!</h1>'})
局部注册
const app = Vue.createApp({
components: {
'component-a': ComponentA,//格式--- 名称:页面
'component-b': ComponentB
}
})
动态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>
五、函数
监听
通过 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>
<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>
样式
除了classs和v-bind:class、:class之外可以使用 $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>
事件处理
事件处理程序中可以有多个方法,这些方法由逗号运算符分隔:
<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>
按键修饰符(点击键盘指定按键触发)
<!-- 只有在 keyCode 是 13 时调用 vm.submit() -->
<input v-on:keyup.13="submit">
<!-- 同上 -->
<input v-on:keyup.enter="submit">
<!-- 缩写语法 -->
<input @keyup.enter="submit">
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button @click.ctrl="onClick">A</button>
<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>
<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button @click.exact="onClick">A</button>
全部的按键别名:
.enter
.tab
.delete (捕获 "删除" 和 "退格" 键)
.esc
.space
.up
.down
.left
.right
系统修饰键:
.ctrl
.alt
.shift
.meta
鼠标按钮修饰符:
.left
.right
.middle
六、表单
基本上就是使用v-model 绑定数据。
单选数据绑定:
picked : 'Runoob'
v-model="picked"
多选数据绑定
checkedNames: []
v-model="checkedNames"
修饰符
.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">
七、路由
Vue.js 路由需要载入 vue-router 库:https://github.com/vuejs/vue-router-next
中文文档地址:https://next.router.vuejs.org/zh/guide/
注册路由方式
<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>
或
// 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')
// 现在,应用已经启动了!
标签属性
<router-view>
router-view 将显示与 url 对应的组件。你可以把它放在任何地方,以适应你的布局。
<router-link>
是一个组件,该组件用于设置一个导航链接,切换不同 HTML 内容。 to 属性为目标地址, 即要显示的内容。
<!-- 字符串 -->
<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 属性的话,当点击时,会调用 router.replace() 而不是 router.push(),导航后不会留下 history 记录。 -->
<router-link :to="{ path: '/abc'}" replace></router-link>
<!-- 设置 append 属性后,则在当前 (相对) 路径前添加其路径。例如,我们从 /a 导航到一个相对路径 b,如果没有配置 append,则路径为 /b,如果配了,则为 /a/b -->
<router-link :to="{ path: 'relative/path'}" append></router-link>
<!-- 有时候想要 <router-link> 渲染成某种标签,例如 <li>。 于是我们使用 tag prop 类指定何种标签,同样它还是会监听点击,触发导航。 -->
<router-link to="/foo" tag="li">foo</router-link>
<!-- 渲染结果 -->
<li>foo</li>
<!-- 设置 链接激活时使用的 CSS 类名。可以通过以下代码来替代。 -->
<router-link v-bind:to = "{ path: '/route1'}" active-class = "_active">Router Link 1</router-link>
<!-- 声明可以用来触发导航的事件。可以是一个字符串或是一个包含字符串的数组。-->
<router-link v-bind:to = "{ path: '/route1'}" event = "mouseover">Router Link 1</router-link>
<!-- 以上代码设置了 event 为 mouseover ,及在鼠标移动到 Router Link 1 上时导航的 HTML 内容会发生改变。-->
八、混入
混入 (mixins)定义了一部分可复用的方法或者计算属性。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。
简单混入
// 定义混入对象
const myMixin = {
created() {
this.hello()
},
methods: {
hello() {
console.log('欢迎来到混入实例-RUNOOB!')
}
}
}
// 定义一个应用,使用混入
const app = Vue.createApp({
mixins: [myMixin]
})
app.mount('#app') // => "欢迎来到混入实例-RUNOOB!"
同名混入处理
- 当组件和混入对象含有同名选项时,这些选项将以恰当的方式混合。
比如,数据对象在内部会进行浅合并 (一层属性深度),在和组件的数据发生冲突时以组件数据优先。 - 同名钩子函数将合并为一个数组,因此都将被调用。另外,mixin 对象的钩子将在组件自身钩子之前调用。
- 值为对象的选项,例如 methods、components 和 directives,将被合并为同一个对象。两个对象键名冲突时,取组件对象的键值对。
全局混入
也可以全局注册混入对象。注意使用! 一旦使用全局混入对象,将会影响到 所有 之后创建的 Vue 实例。使用恰当时,可以为自定义对象注入处理逻辑。
const app = Vue.createApp({
myOption: 'hello!'
})
// 为自定义的选项 'myOption' 注入一个处理器。
app.mixin({
created() {
const myOption = this.$options.myOption
if (myOption) {
document.write(myOption)
}
}
})
app.mount('#app') // => "hello!"
九、网络请求Ajax(axios)
https://github.com/axios/axios
使用样例
Vue.axios.get(api).then((response) => {
console.log(response.data)
}).catch(function (error) { // 请求失败处理
console.log(error);
}
this.axios.get(api).then((response) => {
console.log(response.data)
})
this.$http.get(api).then((response) => {
console.log(response.data)
})
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]])
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
axios
.post('https://www.runoob.com/try/ajax/demo_axios_post.php')
.then(response => (this.info = response))
.catch(function (error) { // 请求失败处理
console.log(error);
});
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.all(iterable)
axios.spread(callback)
配置
配置会以一个优先顺序进行合并。这个顺序是:在 lib/defaults.js 找到的库的默认值,然后是实例的 defaults 属性,最后是请求的 config 参数。后者将优先于前者。
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';
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]])
// 添加请求拦截器
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);
// 可以使用 validateStatus 配置选项定义一个自定义 HTTP 状态码的错误范围。
axios.get('/user/12345', {
validateStatus: function (status) {
return status < 500; // 状态码在大于或等于500时才会 reject
}
})
相关参数说明
{
// `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 >= 200 && status < 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) {
})
}
Response:
{
// `data` 由服务器提供的响应
data: {},
// `status` HTTP 状态码
status: 200,
// `statusText` 来自服务器响应的 HTTP 状态信息
statusText: "OK",
// `headers` 服务器响应的头
headers: {},
// `config` 是为请求提供的配置信息
config: {}
}
取消请求
可以使用同一个 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.');
或者
var CancelToken = axios.CancelToken;
var cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
// executor 函数接收一个 cancel 函数作为参数
cancel = c;
})
});
// 取消请求
cancel();
其他
请求时使用 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里, 可以使用 querystring 模块或者qs 库:
const querystring = require('querystring');
axios.post('http://something.com/', querystring.stringify({ foo: 'bar' }));
Promises
axios 依赖原生的 ES6 Promise 实现而被支持。http://caniuse.com/promises
如果你的环境不支持 ES6 Promise,你可以使用 polyfill。https://github.com/jakearchibald/es6-promise
TypeScript支持
import axios from "axios";
axios.get("/user?ID=12345");
十、组合式API
组合式 API(Composition API) 主要用于在大型组件中提高代码逻辑的可复用性。
Vue3 使用组合式 API 的地方为 setup。
在 setup 中,我们可以按逻辑关注点对部分代码进行分组,然后提取逻辑片段并与其他组件共享代码。因此,组合式 API(Composition API) 允许我们编写更有条理的代码。
样例
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} // 返回的函数与方法的行为相同
}
}
ref() 函数可以根据给定的值来创建一个响应式的数据对象,返回值是一个对象,且只包含一个 .value 属性。
在 setup() 函数内,由 ref() 创建的响应式数据返回的是对象,所以需要用 .value 来访问。
下表为 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 |
模板引用
在使用组合式 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 中的用法
<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>
侦听模板引用
<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>