Vue组件基础和Ref对象
组件
概念: 组件系统是 Vue 的一个重要概念,Vue允许我们将代码拆分独立成一些将小型的可复用模块。这些模块就被成为组件,使用这些组件就可以构建一个大型项目。在Vue中组件分为全局组件和私有组件
组件的创建
- 全局组件
概念: 注册在Vue构造函数中的组件,在Vue实例任何地方任何组件中都可以使用
语法: Vue.component('组件名', {组件的配置对象})
- 私有组件
概念: 通过components配置选项注册在Vue实例对象或其他子组件内部,只能在注册的父组件内部使用。
语法: Vue配置选项和组件配置选项都支持components属性 components: {组件名: { 组件配置选项 }}
注意: template属性指定DOM模板结构中必须有且仅有一个根DOM元素!
组件的data配置选项
概念: 组件设计初衷就是将哪些独立的可复用的代码块封装起来,因为对象是引用数据类型如果直接将组件的data属性设置为对象的话。同一个组件在复用时会导致多个组件同时读写同一个对象,严重的影响了组件可复用性和独立性。为了解决这个问题Vue明确规定组件的data不可以是个对象,而是一个返回data对象的工厂模式函数。
语法:
// 普通函数
Vue.component('gec-title', {
template: `
{{name}}
`,
data() {
return {
name: '小明',
age: 18
}
}
})
// 箭头函数
Vue.component('gec-title', {
template: `
{{name}}
`,
data: () => ({
name: '小明',
age: 18
})
})
单项数据流
概念: 在Vue中组件之间是单项数据流的。单项数据流规定子组件不可以直接访问父组件的数据,只能通过props属性让父组件把数据传递给子组件。并且子组件不可以直接修改父组件传递给子组件的数据。
props的使用
概念: 组件可以通过特殊的配置选项props给自身设置自定义属性,父组件就可通过props属性传值将父组件的数据传递给子组件,因为Vue是单项数据流子组件不可以修改props(props是只读的)
注意:组件的data属性与props属性不能同名
{{age}}
反向传值
因为Vue是单项数据流的,规定只允许父组件向子组件传递状态,而不允许子组件直接修改父组件传递过来的状态。Vue提供了自定义事件API,通过父组件监听子组件自定义事件, 当子组件想要修改父组件的状态时会通过 $emit 方法触发自定义事件。父组件对应监听子组件自定义事件的回调函数就会被触发,这样父组件自身的方法就会相应的去修改自身的状态。子组件的props就会更新
语法: $emit('自定义事件名', 向父组件回调函数传递参数)
注意: $emit只接受两个参数,参数一触发父组件监听的指定事件名,参数二(可选)向父组件监听事件回调函数传递的数据
案例: $emit的使用
Vue脚手架安装使用
- Step1 在系统变量中安装Vue脚手架工具vue-cli
#全局安装vue-cli环境配置
npm install -g @vue/cli
[图片上传失败...(image-f5a03d-1623249421811)]
- Step2访问到指定目录,使用vue指令创建新项目
#全局安装完毕后,以后 vue create 项目名称 搭建项目
vue create project_name
[图片上传失败...(image-3c5470-1623249421811)]
- Step3执行创建vue项目指令时会返回一个vue项目配置询问,先直接使用默认vue 2.0 模板
? Please pick a preset: (Use arrow keys)
> Default ([Vue 2] babel, eslint) #使用默认vue 2.0 模板
Default (Vue 3 Preview) ([Vue 3] babel, eslint) #使用默认vue 3.0 模板
Manually select features #自定义模板
[图片上传失败...(image-58dc1f-1623249421811)]
- Step4 安装完毕后
# 访问新创建的项目目录
cd project_name
#启动测试用服务开发指令
npm run serve
#项目开发完毕打包指令
npm run build
Vue-cli项目结构
webpack-demo
|- /public // 公共目录,这个目录中的文件不会被webpack打包而是作为一个静态目录
// 放置在 public 目录下或通过绝对路径被引用。这类资源将会直接被拷贝,而不会经过 webpack 的处理。
|- index.html // vue中 html模板文件
|- /src // 整个项目代码开发目录
|- main.js // 项目的入口文件
|- babel.config.js // webpack babel-loader配置文件
|- package.json // 项目的配置描述文件
|- README.md // 项目的readme文件
+|- vue.config.js // 可自定义Vue webpack相关配置的文件
局部关闭 ESlint 校检
ESlint 是一个js代码检测工具,约束开发人员的代码风格,如果想具体了解,请查阅其文档
相关文档: https://blog.csdn.net/qq_39557024/article/details/107519531
public 文件夹
概念: 任何放置在 public 文件夹的静态资源都会被简单的复制,而不经过 webpack。你需要通过绝对路径来引用它们。
注意: 我们推荐将资源作为你的模块依赖图的一部分导入,这样它们会通过 webpack 的处理并获得如下好处:
脚本和样式表会被压缩且打包在一起,从而避免额外的网络请求。
文件丢失会直接在编译时报错,而不是到了用户端才产生 404 错误。
最终生成的文件名包含了内容哈希,因此你不必担心浏览器会缓存它们的老版本。
public 目录提供的是一个应急手段,当你通过绝对路径引用它时,留意应用将会部署到哪里。如果你的应用没有部署在域名的根部,那么你需要为你的 URL 配置 publicPath 前缀,在 public/index.html 或其它通过 html-webpack-plugin 用作模板的 HTML 文件中,你需要通过 <%= BASE_URL %> 设置链接前缀:
在js文件中 使用process.env.BASE_URL作为pubulic文件的前缀
// 直接引入静态目录中的文件
// 相对路径的引入会导致webpack对该文件进行打包
publicPath配置实在项目的根目录下vue.config.js中设置publicPath选项就好了
module.exports = {
publicPath: process.env.NODE_ENV === 'production'
? '/production-sub-path/' //真实开发的话,如果你的项目存放在公司域名二级路径下 只需要将 /production-sub-path/改为 /公司二级路径/就可以了
: '/'
}
webpack 相关(了解)
介绍: 在vue-cli中简单的配置webpack方式就是在Vue项目的根目录下创建vue.config.js文件。这个通过配置这个文件中 module.exports 公开的对象实现对Vue Webpack进行修改。
- 指定项目静态资源模块的相对路径
- 指定打包后文件的目录(默认dist文件)
- assets文件的目录
- 多页面应用开发
- css模块化 loader等
- 给项目添加Webpack plugin
- 配置当前项目的开发测试服务器
// vue.config.js
module.exports = {
publicPath: process.env.NODE_ENV === 'production'
? '/production-sub-path/' //真实开发的话,如果你的项目存放在公司域名二级路径下 只需要将 /production-sub-path/改为 /公司二级路径/就可以了
: '/',
devServer: { // 服务器代理,当请求了代理设置的路径时 会自动跳转到指定服务器上,解决跨域问题
proxy: {
"/search": {
target: 'http://musicapi.leanapp.cn/',
changeOrigin: true
// 当你请求 /search?123123 时 会代理到 'http://musicapi.leanapp.cn/search?123123'
}
}
}
}
了解Vue-cli src目录结构
介绍: 在src文件中main.js是整个项目的入口文件,也是实例化Vue对象的地方
因为vue-cli是模块化开发,所以整个项目不适用< script >标签引入Vue支持而是使用模块化依赖模式通过import引入Vue对象
import Vue from 'vue'
import App from './App.vue' // 引入单文件App组件
Vue.config.productionTip = false
// 实例化Vue
new Vue({
render: h => h(App)
// render是template字符串模板的替代方案,render是一个函数函数接收一个参数(createElement)
// 这个参数可以将组件生成为一个Vue DOM节点渲染在页面上
// 所以上面这句化等价于 template: ' '
}).$mount('#app')
// 如果 Vue 实例在实例化时没有收到 el 选项,则它处于“未挂载”状态,没有关联的 DOM 元素。
// 这时可以使用 vm.$mount() 手动地挂载一个未挂载的实例。
认识单文件组件
我们观察下面的代码,在Vue实例中添加了局部组件App,如果说除了Vue实例以外其他组件可能也需要注册App局部组件的话。我们推荐将App抽离出来提供给其他组件复用
new Vue({
el: '#app',
components: {
App: {
props: ['name','age'],
data() {
return {
address: 'bj'
}
},
template: `
用户:{{name}} 年龄:{{age}}
地址:{{address}}
`
}
}
})
抽离后
const App = {
props: ['name','age'],
data() {
return {
address: 'bj'
}
},
template: `
用户:{{name}} 年龄:{{age}}
地址:{{address}}
`
}
new Vue({
el: '#app',
components: {
App
}
})
我们在开发中推荐使用模块化开发,建议将每个可复用的组件单独封装成一个js模块,通过import引入其他文件中复用。这样的好处是便于维护更新让项目的结构更明确。
// 将App组件封装成一个js模块
// App.js
export default {
props: ['name','age'],
data() {
return {
address: 'bj'
}
},
template: `
用户:{{name}} 年龄:{{age}}
地址:{{address}}
`
}
// 其他组件或实例注册App组件
import App from './App'
new Vue({
el: '#app',
components: {
App
}
})
Vue为了简化template字符串模板的开发(使用字符串写HTML语法js无法格式化代码,编译器无法对模板进行补全和检查),Vue提供了一个.vue文件简化了js文件创建单文件组件时template字符串模板开发不便。.vue文件单独的将template选项抽离出来以HTML的形式进行开发。
上面的App.js就可以转化为:
// App.vue
// template 被抽离出来变成了一个独立的标签,内部本来使用字符串模板代码变成HTML语法
用户:{{name}} 年龄:{{age}}
地址:{{address}}
注意:
-
.vue
文件不仅支持template标签指定组件的模板样式和script 公开当前模板配置选项,还支持style标签内置的设置当前组件的样式,而且style标签支持使用sass\less\stylus预编译语言
我是Demo,在vue文件中
- 建议使用.vue创建组件时,组件名与文件名一致并且使用每个单词首字母大写的命名方法。例: HelloWorld.vue、DemoComponent.vue
Props验证
概念:在开发中我们可以为组件的 prop 指定验证要求,例如你知道的这些类型。如果有一个需求没有被满足,则 Vue 会在浏览器控制台中警告你。这一模式会在开发中帮开发人员捕获大量的异常。
语法: props可以是一个数组,数组中的每一项都是当前组件的props属性名。props属性还是是一个对象为每个指定的props属性指定其验证规则以及默认值
props: {
// 当前组件的propsA属性必须是Number数据类型或者为空(可忽略)
propA: Number,
// propB与propA等价
// prop为指定数据类型只需要设置该属性type值为对应数据类型的构造函数
propB: {
type: Number
},
// prop属性可以设置为必要属性,渲染该组件时必须给该属性传值并且不可为空
propC: {
type: Number,
required: true
},
// prop属性支持默认值,当没有给该prop传递属性是,该属性则会使用默认值
// 注意: 默认值优先级小于required
propD: {
type:Number,
default: 15
},
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
}
}
Props验证规则
props: {
// 通过构造函数指定prop数据类型
propA: Number,
propB: Boolean,
propC: String,
propD: Array,
propE: Object,
propF: Symbol,
propG: RegExp,
propH: Date,
propI: Cat, // 可以指定任何构造函数作为prop的type,当前prop接收到的值必须是当前指定构造函数的实例对象
propJ: Function
// prop验证支持 多个可能的类型
propK: [String, Number],
// 自定义验证规则,不满足验证条件是return false
propL: {
validator(val) {// val 传入到 propL属性的值
if (typeof val === "string") {
if (/fuck/gi.test(val)) {
console.error("组件ComponentA中 属性 PropL包含敏感词汇!");
} else {
return true;
}
} else {
console.error("数据必须是字符串");
}
return false;
}
}
}
动态组件
概念: VUe 提供了一个标签component,该标签可以使用 is attribute 来切换不同的组件:
在动态组件上使用 keep-alive
概念: 当在这些组件之间切换的时候,你有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题。
注意: 在上面的案例中如果没有使用keep-alive缓存失活的组件,那么失活的组件将被丢弃(卸载),组件切换时每次都是挂载一个全新的组件
异步组件(了解)
介绍: 在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。为了简化,Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。例如:
Vue.component('async-example', function (resolve, reject) { setTimeout(function () { // 向 `resolve` 回调传递组件定义 resolve({ template: '
I am async!' }) }, 1000) })如你所见,这个工厂函数会收到一个 resolve 回调,这个回调函数会在你从服务器得到组件定义的时候被调用。你也可以调用 reject(reason) 来表示加载失败。这里的 setTimeout 是为了演示用的,如何获取组件取决于你自己。一个推荐的做法是将异步组件和 webpack 的 code-splitting 功能一起配合使用:
Vue.component('async-webpack-example', function (resolve) { // 这个特殊的 `require` 语法将会告诉 webpack // 自动将你的构建代码切割成多个包,这些包 // 会通过 Ajax 请求加载 require(['./my-async-component'], resolve) })
你也可以在工厂函数中返回一个 Promise,所以把 webpack 2 和 ES2015 语法加在一起,我们可以这样使用动态导入:
Vue.component( 'async-webpack-example', // 这个动态导入会返回一个 `Promise` 对象。 () => import('./my-async-component') )
当使用局部注册的时候,你也可以直接提供一个返回 Promise 的函数:
new Vue({ // ... components: { 'my-component': () => import('./my-async-component') } })
如果你是一个 Browserify 用户同时喜欢使用异步组件,很不幸这个工具的作者明确表示异步加载“并不会被 Browserify 支持”,至少官方不会。Browserify 社区已经找到了一些变通方案,这些方案可能会对已存在的复杂应用有帮助。对于其它的场景,我们推荐直接使用 webpack,以拥有内置的头等异步支持。
懒加载状态 (2.3.0+ 新增)
这里的异步组件工厂函数也可以返回一个如下格式的对象:
const AsyncComponent = () => ({ // 需要加载的组件 (应该是一个 `Promise` 对象) component: import('./MyComponent.vue'), // 异步组件加载时使用的组件 loading: LoadingComponent, // 加载失败时使用的组件 error: ErrorComponent, // 展示加载时组件的延时时间。默认值是 200 (毫秒) delay: 200, // 如果提供了超时时间且组件加载也超时了, // 则使用加载失败时使用的组件。默认值是:`Infinity` timeout: 3000 })
Ref
概念: Vue给组件元素提供了一个ref属性,绑定了ref属性的组件元素可以在当前vue实例对象中通过$refs访问其真实DOM节点(标签元素)或实例对象(组件)。
在原生html标签中使用ref
语法: 当前组件内部 this.$refs.变量名 访问其真实DOM节点
在组件中中使用ref
语法:
当前组件内部 this.$refs.变量名 访问这个子组件的实例对象,可以获取这个组件实例的属性和方法(尽量避免使用该模式,因为他会增加组件间的耦合性)
注意:
- vue实例中每个标签或组件都可以设置ref属性,绑定了ref属性的元素可以有任意个
text
// 这时可以在组件中通过 this.$refs不同的变量名访问对应的DOM元素
- ref配合列表渲染(v-for)使用时,$refs返回值为包含列表渲染出来的所有元素的一个数组
// 与v-for同级的ref 返回值都是列表渲染出来当前元素数组集合
//v-for 内部的ref 返回值也是列表渲染出来当前元素数组集合
{{num}}-span
没用的a
{{num}}