组件可以扩展 HTML 元素,封装可重用的代码
在较高层面上,组件是自定义元素, Vue.js 的编译器为它添加特殊功能
在有些情况下,组件也可以是原生 HTML 元素的形式,以 is 特性扩展。
<div id="example"> <my-component>my-component> div>
// 注册全局组件,指定之前设定的元素名,然后传入对象 Vue.component('my-component', { template: 'A custom component!' }) // 创建根实例 new Vue({ el: '#example' })
不必在全局注册每个组件。通过使用组件实例选项注册,可以使组件仅在另一个实例/组件的作用域中可用
//将传入给组件的对象单独写 var Child = { template: 'A custom component!' } new Vue({ //通过components语法创建局部组件 //将组件仅仅放在这个vue实例里面使用 components: { // 将只在父模板可用 'my-component': Child } })
当使用 DOM 作为模版时(例如,将 el 选项挂载到一个已存在的元素上), 你会受到 HTML 的一些限制,
因为 Vue 只有在浏览器解析和标准化 HTML 后才能获取模版内容。尤其像这些元素 , , , 限制了能被它包裹的元素, 只能出现在其它元素内部。 <table> <my-row>...my-row> table> <table> <tr is="my-row">tr> table> data必须是函数 使用组件时,大多数可以传入到 Vue 构造器中的选项可以在注册组件时使用,有一个例外: data 必须是函数。 实际上 //这样会报错,提示data必须是一个函数 Vue.component('my-component', { template: '{{ message }}', data: { message: 'hello' } }) <div id="example-2"> <simple-counter>simple-counter> <simple-counter>simple-counter> <simple-counter>simple-counter> div> var data = { counter: 0 } Vue.component('simple-counter', { template: '{{ counter }}', // data 是一个函数,因此 Vue 不会警告, // 但是我们为每一个组件返回了同一个对象引用,所以改变其中一个会把其他都改变了 data: function () { return data } }) new Vue({ el: '#example-2' }) 避免出现同时改变数据的情况 //返回一个新的对象,而不是返回同一个data对象引用 data: function () { return { //字面量写法会创建新对象 counter: 0 } } 构成组件 组件意味着协同工作,通常父子组件会是这样的关系: 组件 A 在它的模版中使用了组件 B 。它们之间必然需要相互通信 父组件要给子组件传递数据,子组件需要将它内部发生的事情告知给父组件 然而,在一个良好定义的接口中尽可能将父子组件解耦是很重要的。这保证了每个组件可以在相对隔离的环境中书写和理解,也大幅提高了组件的可维护性和可重用性。 在 Vue.js 中,父子组件的关系可以总结为 props down, events up 。 父组件通过 props 向下传递数据给子组件,子组件通过 events 给父组件发送消息。看看它们是怎么工作的。 prop 使用prop传递数据 组件实例的作用域是孤立的。这意味着不能并且不应该在子组件的模板内直接引用父组件的数据。 使用 props 把数据传给子组件。 prop 是父组件用来传递数据的一个自定义属性 子组件需要显式地用 props 选项声明 “prop” <div id="example-2"> <child message="hello!">child> div> Vue.component('child', { // 声明 props,用数组形式的对象 props: ['message'], // 就像 data 一样,prop 可以用在模板内 // 同样也可以在 vm 实例中像 “this.message” 这样使用 template: '{{ message }}' }); new Vue({ el: '#example-2' }) 动态prop 用 v-bind 动态绑定 props 的值到父组件的数据中。每当父组件的数据变化时,该变化也会传导给子组件 <div id="example-2"> <input v-model="parentMsg"> <br> <child v-bind:my-message="parentMsg">child> div> Vue.component('child', { // 声明 props props: ['my-message'], template: '{{ myMessage }}' //如果写my-message会报错,需要转换为驼峰写法 }); new Vue({ el: '#example-2', data: { parentMsg: '' } }) 短横线和驼峰写法 HTML 特性不区分大小写。当使用非字符串模版时,prop的名字形式会从 camelCase 转为 kebab-case(短横线隔开) 在JavaScript里面使用驼峰写法,但是在html里面需要转成短横线写法 反之亦然,vue会自动处理来自html的短横线写法转为驼峰写法 字面量语法和动态语法 <comp some-prop="1">comp> <comp v-bind:some-prop="1">comp> 单向数据流 prop 是单向绑定的 当父组件的属性变化时,将传导给子组件,但是不会反过来。这是为了防止子组件无意修改了父组件的状态——这会让应用的数据流难以理解。 每次父组件更新时,子组件的所有 prop 都会更新为最新值。这意味着你不应该在子组件内部改变 prop 。如果你这么做了,Vue 会在控制台给出警告。 通常有两种改变 prop 的情况: prop 作为初始值传入,子组件之后只是将它的初始值作为本地数据的初始值使用 定义一个局部 data 属性,并将 prop 的初始值作为局部数据的初始值。 <div id="example-2"> <child initial-counter="10">child> div> Vue.component('child', { props: ['initialCounter'],//这里用驼峰写法 data: function () { //转为一个局部变量,写一个data对象给组件使用 return {counter: this.initialCounter} }, template: '{{ counter }}' }); new Vue({ el: '#example-2' }) prop 作为需要被转变的原始值传入。 定义一个 computed 属性,此属性从 prop 的值计算得出。 //例子没有写完,但是根据第一个例子可以知道利用computed的手法原理其实跟写一个data差不多 props: ['size'], computed: { normalizedSize: function () { return this.size.trim().toLowerCase() } } 注意在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,如果 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态。 prop验证 组件可以为 props 指定验证要求,当组件给其他人使用时这很有用。 Vue.component('example', { props: { // 基础类型检测 (`null` 意思是任何类型都可以) 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 value > 10 } } } }) 自定义事件 每个 Vue 实例都实现了事件接口(Events interface) 使用 $on(eventName) 监听事件 使用 $emit(eventName) 触发事件 父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件。 <div id="counter-event-example"> <p>{{ total }}p> <button-counter v-on:increment1="incrementTotal">button-counter> <button-counter v-on:increment1="incrementTotal">button-counter> div> Vue.component('button-counter', { //监听click事件,处理程序为increment(子组件定义的方法) template: '{{ counter }}', //每一个counter都是独立的对象属性 data: function () { return { counter: 0 } }, //子组件的方法 methods: { increment: function () { this.counter += 1; //在子组件里面直接触发之前监听的increment1事件来执行父组件的方法 this.$emit('increment1'); } }, }) new Vue({ el: '#counter-event-example', data: { total: 0 }, //父组件的方法 methods: { incrementTotal: function () { this.total += 1 } } }) 1.组件之间因为作用域不同的关系,所以相互独立,所以子组件想要使用父组件的方法的话需要做一个新的监听映射 给组件绑定原生事件 <my-component v-on:click.native="doTheThing">my-component> 使用自定义事件的表单输入组件 自定义事件也可以用来创建自定义的表单输入组件,使用 v-model 来进行数据双向绑定。所以要让组件的 v-model 生效,它必须: 接受一个 value 属性 在有新的 value 时触发 input 事件 <input v-model="something"> <input v-bind:value="something" v-on:input="something = $event.target.value"> 一个非常简单的货币输入: <currency-input v-model="price">currency-input> Vue.component('currency-input', { template: '\ \ $\ \ \ ', props: ['value'], //父组件将绑定的value传给子组件 methods: { // 不是直接更新值,而是使用此方法来对输入值进行格式化和位数限制 updateValue: function (value) { var formattedValue = value //对值进行处理 // 删除两侧的空格符 .trim() // 保留 2 小数位和2位数 .slice(0, value.indexOf('.') + 3) // 如果值不统一,手动覆盖以保持一致,为了保持输入框显示内容跟格式化内容一致 if (formattedValue !== value) { //因为注册是一个input元素,所以this.$refs 就是input元素 this.$refs.input.value = formattedValue } //手动触发input事件,将格式化后的值传过去,这是最终显示输入框的输出 this.$emit('input', Number(formattedValue)) } } }) //实例化vue实例的 new Vue({ el: '#aa', //要绑定一个vue实例,例如包裹一个id为aa的div data:{ price:'' //v-model要有数据源 } }) ref 被用来给元素或子组件注册引用信息。引用信息会根据父组件的 $refs 对象进行注册。如果在普通的DOM元素上使用,引用信息就是元素; 如果用在子组件上,引用信息就是组件实例 ref 这是一个比较完整的例子: <div id="app"> <currency-input label="Price" v-model="price" >currency-input> <currency-input label="Shipping" v-model="shipping" >currency-input> <currency-input label="Handling" v-model="handling" >currency-input> <currency-input label="Discount" v-model="discount" >currency-input> <p>Total: ${{ total }}p> div> Vue.component('currency-input', { template: '\ \ {{ label }}\ $\ \ \ ', props: { //多个prop传递,因为prop是对象,只要是对象格式就行 value: { type: Number, default: 0 }, label: { type: String, default: '' } }, mounted: function () { //这是vue的过渡状态,暂时忽略不影响理解 this.formatValue() }, methods: { updateValue: function (value) { var result = currencyValidator.parse(value, this.value) if (result.warning) { // 这里也使用了$refs获取引用注册信息 this.$refs.input.value = result.value } this.$emit('input', result.value) }, formatValue: function () { this.$refs.input.value = currencyValidator.format(this.value) //这里注意下,这个this是prop传递过来的,也相当于这个组件作用域 }, selectAll: function (event) { //event可以获取原生的js事件 // Workaround for Safari bug // http://stackoverflow.com/questions/1269722/selecting-text-on-focus-using-jquery-not-working-in-safari-and-chrome setTimeout(function () { event.target.select() }, 0) } } }) new Vue({ el: '#app', data: { price: 0, shipping: 0, handling: 0, discount: 0 }, computed: { total: function () { return (( this.price * 100 + this.shipping * 100 + this.handling * 100 - this.discount * 100 ) / 100).toFixed(2) } } }) 非父子组件通信 在简单的场景下,使用一个空的 Vue 实例作为中央事件总线: var bus = new Vue() // 触发组件 A 中的事件 bus.$emit('id-selected', 1) /* 通过on来监听子组件的事件来实现传递 */ // 在组件 B 创建的钩子中监听事件 bus.$on('id-selected', function (id) { // ... }) 使用Slot分发内容 为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板。这个过程被称为 内容分发 (或 “transclusion” 如果你熟悉 Angular) 编译作用域 组件作用域简单地说是:父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译。假定 someChildProperty 是子组件的属性,上例不会如预期那样工作。父组件模板不应该知道子组件的状态。 <child-component v-show="someChildProperty">child-component> 如果要绑定子组件内的指令到一个组件的根节点,应当在它的模板内这么做: Vue.component('child-component', { // 有效,因为是在正确的作用域内 template: 'Child', data: function () { return { //因为这个属性在当前组件内编译(创建了) someChildProperty: true } } }) 类似地,分发内容是在父组件作用域内编译。 单个Slot 除非子组件模板包含至少一个 插口,否则父组件的内容将会被丢弃。 当子组件模板只有一个没有属性的 slot 时,父组件整个内容片段将插入到 slot 所在的 DOM 位置,并替换掉 slot 标签本身。 备用内容在子组件的作用域内编译,并且只有在宿主元素为空,且没有要插入的内容时才显示备用内容。 <div id="aa"> <h1>我是父组件的标题h1> <my-component>my-component> <my-component> <p>这是一些初始内容p> <p>这是更多的初始内容p> my-component> div> Vue.component('my-component', { //my-component 组件有下面模板 template: '\ \ 我是子组件的标题 \ \ //有slot插口,所以没有被父组件丢弃 只有在没有要分发的内容时才会显示。\ \ \ ' }) new Vue({ el: '#aa', }) 渲染结果: <div id="aa"><h1>我是父组件的标题h1> <div> <h2>我是子组件的标题h2> 只有在没有要分发的内容时才会显示。 div> <div> <h2>我是子组件的标题h2> <p>这是一些初始内容p> <p>这是更多的初始内容p> div> div> 有名字的Slot 元素可以用一个特殊的属性 name 来配置如何分发内容。多个 slot 可以有不同的名字。具名 slot 将匹配内容片段中有对应 slot 特性的元素。 仍然可以有一个匿名 slot ,它是默认 slot ,作为找不到匹配的内容片段的备用插槽。如果没有默认的 slot ,这些找不到匹配的内容片段将被抛弃。 <div id="aa"> <app-layout> <h1 slot="header">这里可能是一个页面标题h1> <p>主要内容的一个段落。p> <p>另一个主要段落。p> <p slot="footer">这里有一些联系信息p> app-layout> div> Vue.component('app-layout', { template: '\ \ \ //找到名字叫header的slot之后替换内容,这里替换的是整个DOM \ \ \ //因为slot没有属性,会将内容插入到slot的所在的DOM位置 \ \ \ \ ' }); new Vue({ el: '#aa', }) 渲染结果为: <div class="container"> <header> <h1>这里可能是一个页面标题h1> header> <main> <p>主要内容的一个段落。p> <p>另一个主要段落。p> main> <footer> <p>这里有一些联系信息p> footer> div> 作用域插槽(vue2.1) 作用域插槽是一种特殊类型的插槽,用作使用一个(能够传递数据到)可重用模板替换已渲染元素。 在子组件中,只需将数据传递到插槽,就像你将 prop 传递给组件一样 在父级中,具有特殊属性 scope 的 元素,表示它是作用域插槽的模板。scope 的值对应一个临时变量名,此变量接收从子组件中传递的 prop 对象 <div id="parent" class="parent"> <child> <template scope="props"> <span>hello from parentspan> <span>{{ props.text }}span> template> child> div> Vue.component('child', { props: ['props'], //这个写不写都可以,作用域插槽固定会接收prop对象,而且这个prop对象是肯定存在的 template: '\ \ \ //在子组件里直接将数据传递给slot \ ' }); new Vue({ el: '#parent', }) 渲染结果: <div class="parent"> <div class="child"> <span>hello from parentspan> <span>hello from childspan> div> div> 另外一个例子,作用域插槽更具代表性的用例是列表组件,允许组件自定义应该如何渲染列表每一项 <div id="parent"> <my-awesome-list :items="items"> <template slot="item" scope="props"> <li class="my-fancy-item">{{ props.text }}li> template> my-awesome-list> div> Vue.component('my-awesome-list', { props:['items'], //需要声明prop为items,需要是为下面的循环遍历的items的数据源做设定,位置3 template: '\ \ \ //在slot中,循环遍历输出items的text,位置4 \ \ ' }); new Vue({ el: '#parent', data : { items:[ //初始化items数据 {text:"aa"}, {text:"bb"} ] } }) 位置1,实现了一个组件的prop绑定,prop需要在组件里面声明,这里绑定的是items,这是要将父组件的items传递到子组件,所以在位置3里面需要声明,在vue实例要初始化 位置2,这里scope的props是代表作用域插槽接收来自prop对象的数据,props.text是代表每一个li要输出的是prop对象的text属性 位置3,在组件里声明props,为了接收父组件绑定的items属性,然后将其给位置4的循环使用 位置4,这里绑定了text属性,就是前呼位置2里面输出的prop对象的text属性 动态组件 多个组件可以使用同一个挂载点,然后动态地在它们之间切换。使用保留的 元素,动态地绑定到它的 is 特性 var vm = new Vue({ el: '#example', data: { currentView: 'home' //默认值 }, components: { //根据不同的值进行不同的组件切换,这里用components写法 home: { /* ... */ }, posts: { /* ... */ }, archive: { /* ... */ } } }) <component v-bind:is="currentView"> component> keep-alive 如果把切换出去的组件保留在内存中,可以保留它的状态或避免重新渲染。为此可以添加一个 keep-alive 指令参数 <keep-alive> <component :is="currentView"> component> keep-alive> 杂项 编写可复用组件 在编写组件时,记住是否要复用组件有好处。一次性组件跟其它组件紧密耦合没关系,但是可复用组件应当定义一个清晰的公开接口。Vue 组件的 API 来自三部分 - props, events 和 slots : Props 允许外部环境传递数据给组件 Events 允许组件触发外部环境的副作用 Slots 允许外部环境将额外的内容组合在组件中。 <my-component :foo="baz" :bar="qux" @event-a="doThis" @event-b="doThat" > <img slot="icon" src="..."> <p slot="main-text">Hello!p> my-component> 子组件索引 尽管有 props 和 events ,但是有时仍然需要在 JavaScript 中直接访问子组件。为此可以使用 ref 为子组件指定一个索引 ID 。 <div id="parent"> <user-profile ref="profile">user-profile> div> var parent = new Vue({ el: '#parent' }) // 访问子组件 var child = parent.$refs.profile 当 ref 和 v-for 一起使用时, ref 是一个数组或对象,包含相应的子组件。 $refs 只在组件渲染完成后才填充,并且它是非响应式的。它仅仅作为一个直接访问子组件的应急方案——应当避免在模版或计算属性中使用 $refs 。 ref 被用来给元素或子组件注册引用信息。引用信息会根据父组件的 $refs 对象进行注册。如果在普通的DOM元素上使用,引用信息就是元素; 如果用在子组件上,引用信息就是组件实例 ref 组件命名约定 当注册组件(或者 props)时,可以使用 kebab-case ,camelCase ,或 TitleCase 。Vue 不关心这个。 在 HTML 模版中,请使用 kebab-case 形式: // 在组件定义中 components: { // 使用 kebab-case 形式注册--横线写法 'kebab-cased-component': { /* ... */ }, // register using camelCase --驼峰写法 'camelCasedComponent': { /* ... */ }, // register using TitleCase --标题写法 'TitleCasedComponent': { /* ... */ } } <kebab-cased-component>kebab-cased-component> <camel-cased-component>camel-cased-component> <title-cased-component>title-cased-component> 递归组件 组件在它的模板内可以递归地调用自己,不过,只有当它有 name 选项时才可以 当你利用Vue.component全局注册了一个组件, 全局的ID作为组件的 name 选项,被自动设置. //组件可以用name来写名字 name: 'unique-name-of-my-component' //也可以在创建的时候默认添加名字 Vue.component('unique-name-of-my-component', { // ... }) //如果同时使用的话,递归的时候就会不断递归自己,导致溢出 name: 'stack-overflow', template: '' 使用-v-once-的低级静态组件-Cheap-Static-Component 尽管在 Vue 中渲染 HTML 很快,不过当组件中包含大量静态内容时,可以考虑使用 v-once 将渲染结果缓存起来,就像这样: Vue.component('terms-of-service', { template: '\ \ Terms of Service\ ... a lot of static content ...\ \ ' }) v-once只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。 你可能感兴趣的:(vue,2.0,vue2.0-组件,vue2.0) (二)SAP Group Reporting (GR) 核心子模块功能及数据流向架构解析 数据如何从子公司流转到合并报表的全过程,即数据采集→合并引擎→报表输出,特别是HANA内存计算如何优化传统ETL瓶颈。SAPGroupReporting(GR)核心模块功能及数据流向的架构解析,涵盖核心组件、数据处理流程和关键集成点,适用于S/4HANA1809+版本:一、核心功能模块概览模块功能关键事务码/FioriApp数据采集(DataCollection)整合子公司财务数据(SAP/非SA 实时数据流计算引擎Flink和Spark剖析 程小舰 flinkspark数据库kafkahadoop 在过去几年,业界的主流流计算引擎大多采用SparkStreaming,随着近两年Flink的快速发展,Flink的使用也越来越广泛。与此同时,Spark针对SparkStreaming的不足,也继而推出了新的流计算组件。本文旨在深入分析不同的流计算引擎的内在机制和功能特点,为流处理场景的选型提供参考。(DLab数据实验室w.x.公众号出品)一.SparkStreamingSparkStreamin 深入解析JVM工作原理:从字节码到机器指令的全过程 一、JVM概述Java虚拟机(JVM)是Java平台的核心组件,它实现了Java"一次编写,到处运行"的理念。JVM是一个抽象的计算机器,它有自己的指令集和运行时内存管理机制。JVM的主要职责:加载:读取.class文件并验证其正确性存储:管理内存分配和垃圾回收执行:解释或编译字节码为机器指令安全:提供沙箱环境限制恶意代码二、JVM架构详解JVM由三个主要子系统组成:1.类加载子系统类加载过程分为 最新阿里四面面试真题46道:面试技巧+核心问题+面试心得 风平浪静如码 前言做技术的有一种资历,叫做通过了阿里的面试。这些阿里Java相关问题,都是之前通过不断优秀人才的铺垫总结的,先自己弄懂了再去阿里面试,不然就是去丢脸,被虐。希望对大家帮助,祝面试成功,有个更好的职业规划。一,阿里常见技术面1、微信红包怎么实现。2、海量数据分析。3、测试职位问的线程安全和非线程安全。4、HTTP2.0、thrift。5、面试电话沟通可能先让自我介绍。6、分布式事务一致性。7、ni 【项目实战】 容错机制与故障恢复:保障系统连续性的核心体系 本本本添哥 004-研效与DevOps运维工具链002-进阶开发能力分布式 在分布式系统中,硬件故障、网络波动、软件异常等问题难以避免。容错机制与故障恢复的核心目标是:通过主动检测故障、自动隔离风险、快速转移负载、重建数据一致性,最大限度减少故障对业务的影响,保障系统“持续可用”与“数据不丢失”。以下从核心机制、实现方式、典型案例等维度展开说明。一、故障检测:及时发现异常节点故障检测是容错的第一步,需通过多维度手段实时感知系统组件状态,确保故障被快速识别。1.健康检查与心 Redis + Caffeine 实现高效的两级缓存架构 周童學 Java缓存redis架构 Redis+Caffeine实现高效的两级缓存架构引言在现代高并发系统中,缓存是提升系统性能的关键组件之一。传统的单一缓存方案往往难以同时满足高性能和高可用性的需求。本文将介绍如何结合Redis和Caffeine构建一个高效的两级缓存系统,并通过三个版本的演进展示如何逐步优化代码结构。项目源代码:github地址、gitee地址两级缓存架构概述两级缓存通常由本地缓存(如Caffeine)和分布式缓 uniapp微信小程序 - 详解微信小程序平台用户授权登录全流程,uniapp v3版本中小程序端开发下用户点击登录后获取手机号/昵称/性别/头像等信息完成登录(提供完整示例代码,一键复制开箱即用) 十一猫咪爱养鱼 前端组件与功能(开箱即用)uniapp常见问题解决uniappvue3uniapp3小程序授权登录微信小程序登录获取用户信息教程获取用户昵称手机号头像信息登录vue3版本小程序平台授权登录uniap小程序端用户登录流程uni完整的小程序平台登录源码 效果图在uniapp微信小程序端开发中,超详细实现用户授权登录完整功能源码,用户授权后获取手机号/昵称/头像/性别等,提供完整思路流程及逻辑讲解。uniappVue3和Vue2都能用,你也可以直接复制粘贴,然后改下参数放到你的项目中去就行。整体思路做功能之前,先来看一下整体流程是 分布式链路追踪系统架构设计:从理论到企业级实践 ma451152002 java分布式系统架构 分布式链路追踪系统架构设计:从理论到企业级实践本文深入探讨分布式链路追踪系统的架构设计原理、关键技术实现和企业级应用实践,为P7架构师提供完整的技术方案参考。目录引言:分布式链路追踪的重要性核心概念与技术原理系统架构设计数据模型与协议标准核心组件架构设计性能优化与扩展性设计企业级实施策略技术选型与对比分析监控与运维体系未来发展趋势P7架构师面试要点引言:分布式链路追踪的重要性微服务架构下的挑战在现 vue element 封装表单 影子信息 vuevue.jsjavascript前端 背景:在前端系统开发中,系统页面涉及到的表单组件比较多,所以进行了简单的封装。封装的包括一些Form表单组件,如下:input输入框、select下拉框、等实现效果:理论知识:表单组件官方链接:点击跳转封装组件:封装组件的思路:不封装element组件,每一个input组件绑定一个form对象,例如官网。简单封装element组件,利用for循环生成form表单的每一项el-form-item。进 Spark SQL架构及高级用法 Aurora_NeAr sparksql架构 SparkSQL架构概述架构核心组件API层(用户接口)输入方式:SQL查询;DataFrame/DatasetAPI。统一性:所有接口最终转换为逻辑计划树(LogicalPlan),进入优化流程。编译器层(Catalyst优化器)核心引擎:基于规则的优化器(Rule-BasedOptimizer,RBO)与成本优化器(Cost-BasedOptimizer,CBO)。处理流程:阶段输入输出关键动 大学社团管理系统(11831) codercode2022 javaspringbootspringechartsspringcloudsentineljava-rocketmq 有需要的同学,源代码和配套文档领取,加文章最下方的名片哦一、项目演示项目演示视频二、资料介绍完整源代码(前后端源代码+SQL脚本)配套文档(LW+PPT+开题报告)远程调试控屏包运行三、技术介绍Java语言SSM框架SpringBoot框架Vue框架JSP页面Mysql数据库IDEA/Eclipse开发四、项目截图有需要的同学,源代码和配套文档领取,加文章最下方的名片哦! 深入剖析 boost::unique_lock<boost::mutex> 程序员乐逍遥 C++Boost库C/C++多线程编程专题C++boost线程锁 在高并发的C++程序中,线程安全是永恒的主题。而boost::unique_lock作为Boost.Thread库中的核心组件,为开发者提供了强大、灵活且异常安全的互斥量管理机制。它不仅是RAII(ResourceAcquisitionIsInitialization)设计模式的典范,更是实现复杂线程同步逻辑的基石。一、从lock_guard的说起在介绍unique_lock之前,我们先回顾其“简 VUE 座位图功能+扩展 NUZGNAW vue.jsjavascript前端 1、通过循环画出页面座位图0"style="display:table;margin:0auto;min-height:472px;position:relative;">{{i.sign}}2、画出右下角的预览图3、编写对应js方法watch:{seatList:{handler(newVal,oldVal){if(this.seatList&&!stringBlank(this.seatLis 构建高性能Web应用:深入Spring WebFlux 李多田 本文还有配套的精品资源,点击获取简介:SpringWebFlux是Spring框架的一部分,支持反应式编程模型,适合高并发和低延迟Web应用。它提供了非阻塞I/O和事件驱动模型,优化了多核处理器资源的使用。SpringWebFlux拥有两种编程模式,核心组件包括WebHandler,RouterFunction,WebFilter,和WebSession。它与高性能服务器集成,并提供反应式HTTP Spring Security OAuth2.0在分布式系统中的安全实践 引言分布式系统架构下,安全认证与授权面临跨服务、高并发、多租户等挑战。SpringSecurity与OAuth2.0的结合为微服务安全提供了标准化解决方案。分布式系统中的安全挑战跨服务身份认证的复杂性令牌管理的可扩展性问题多租户场景下的权限隔离需求防止CSRF、XSS等常见攻击SpringSecurityOAuth2.0核心架构授权服务器设计@EnableAuthorizationServer配置 Spring Boot与云原生:微服务架构的创新实践 tmjpz04412 springkubernetes云原生javagraphql 引言:Spring生态的演进与现状Spring框架的发展历程与核心设计理念当前Spring生态的核心组件(SpringBoot、SpringCloud、SpringData等)行业对Spring生态的依赖与创新需求SpringBoot的创新实践1.自动化配置与启动优化条件装配(@Conditional)的深度定制案例启动类加载机制与类路径扫描优化示例:通过自定义Starter实现快速集成第三方服务 深入了解 Kubernetes(k8s):从概念到实践 目录一、k8s核心概念二、k8s的优势三、k8s架构组件控制平面组件节点组件四、k8s+docker运行前后端分离项目的例子1.准备前端项目2.准备后端项目3.创建k8s部署配置文件4.部署应用到k8s集群在当今云计算和容器化技术飞速发展的时代,Kubernetes(简称k8s)已成为容器编排领域的事实标准。无论是互联网巨头、传统企业还是初创公司,都在广泛采用k8s来管理和部署容器化应用。本文将带 Vue CSR 到 Nuxt 3 SSR 迁移:技术实现与问题解决实录 二倍速播放 前端vue.js 1.迁移动机与技术选型1.1CSR架构的局限性基于Vue3和Vite构建的客户端渲染(CSR)单页应用(SPA)提供了良好的开发体验和用户交互流畅性。但是其核心局限在于:搜索引擎优化(SEO):初始HTML响应仅包含一个根div元素,实际内容由JavaScript在浏览器端动态生成。虽然主流搜索引擎(如Google)能够执行部分JavaScript,但其抓取效率和稳定性不如直接获取完整HTML。非 vue项目 阿什么名字不会重复呢 vue.js前端javascript vue进行配置创建vue项目vuecreateone//one就是项目名字npmielement-ui--Snpmiaxios--savenpmiecharts--save配置main.jsimportVuefrom'vue'importAppfrom'./App.vue'importrouterfrom'./router'importstorefrom'./store'importElement 开发避坑短篇(7):Vue+window.print()打印实践 帧栈 避坑指南vue.js前端elementui 需求vue项目中如何打印当前页面内容?解决办法使用浏览器原生APIwindow.print(),这是最简单直接的方式,通过调用window.print()方法触发浏览器打印功能。在vue项目中直接调用print()方法即可,无须引入任何插件。如下:functionhandlePrint(){setTimeout(_=>print(),500)}打印页面样式控制可以使用@mediaprint来控制 macOS 安装全攻略:从基础到企业级部署 zqmgx13291 macos 引言:macOS安装的技术全景与价值macOS作为苹果生态的核心组件,其安装场景涵盖个人用户的系统重装、开发者的多系统环境搭建、企业级设备的批量部署等。据Apple官方数据,2024年全球活跃Mac设备超2亿台,其中AppleSilicon芯片机型占比达68%,架构差异(ARMvsx86)导致安装流程存在显著区别。本文将系统梳理macOS安装的全流程,从基础的单系统重装到复杂的双系统配置、硬件升级 H5UI微信小程序前端框架实战指南 ai 本文还有配套的精品资源,点击获取简介:H5UI是一个为微信小程序开发设计的前端框架,基于H5技术,提供简洁高效的组件库。框架集成了丰富的UI元素,如按钮、表格、导航栏等,简化了界面布局和交互的实现。通过安装、引入、使用组件和事件绑定四个步骤,开发者可以轻松构建功能齐全的应用。了解性能优化等注意事项对于高效开发同样重要。1.微信小程序前端开发框架介绍微信小程序概述微信小程序是微信官方推出的一种无需下 JAVA后端开发——用 Spring Boot 实现定时任务 1candobetter JAVA开发javaspringboot开发语言 在后端开发中,执行定时任务是一个极其常见的需求,无论是每日的数据报表生成、定时的缓存清理,还是自动化同步第三方数据。借助SpringBoot内置的强大功能,我们只需几个简单的注解,就能实现稳定、可靠且极易维护的定时任务。第一步:开启定时任务的总开关(@EnableScheduling)我们首先要告诉SpringBoot:“嘿,我准备在这个项目里使用定时任务功能了,请帮我把相关的组件都准备好!”这个 Centos7防火墙 会飞的灰大狼 Centos7linux Centos7防火墙前言:本来想在系统那里去说防火墙但防火墙要说的要很多使用单独做一章文章去讲防火墙~~在CentOS7中,防火墙是保障系统网络安全的核心组件,默认采用firewalld作为防火墙管理工具(替代了CentOS6的iptables服务),底层仍基于iptables内核模块实现规则控制。iptablesiptables命令的完整格式较为复杂,其核心结构由命令选项、表(table)、 vue2中实现leader-line-vue连线文章对应字符 小莉爱编程 vuebug记录vue.js前端javascript 效果展示通过点击右边的tag,触发连接操作第一步:获取右边tag展示1.右边的tag列表展示,我这边是分为两个list嵌套的数据结构;{"人员":[{ 基于监听和基于回调的事件处理机制 下雨天不带伞boy 基于监听的事件处理机制流程模型图文字描述:事件监听机制中由事件源,事件,事件监听器三类对象组成处理流程如下:Step1:为某个事件源(组件)设置一个监听器,用于监听用户操作Step2:用户的操作,触发了事件源的监听器Step3:生成了对应的事件对象Step4:将这个事件源对象作为参数传给事件监听器step5:事件监听器对事件对象进行判断,执行对应的事件处理器(对应事件的处理方法)基于回调的事件处理 时间组件库Day.js那些事 前端小白花 javascript开发语言ecmascript 一、简介1.什么是Day.jsDay.js是一个轻量级,易于使用的JavaScript日期库,提供了强大的日期和时间处理功能,与Moment.js的API设计相似,但具有更高的性能和更小的体积。Day.js官网https://day.js.org/docs/zh-CN/installation/installation2.优势a.特点轻量级:Dayjs的压缩后大小只有2KB左右,远小于Moment 【antdv4.0FormItem更新tooltip属性引发的思考】 北京时间2023-10-19,ant-design-vue最近发布了4.0.4-4.0.6的小版本更新,其中4.0.4新增了对Form组件中FormItem的tooltip属性支持,这个功能我觉得在后台管理场景下还是非常实用的,tooltip属性作用于FormItem中的FormItemLabel区域,用来配置提示信息,当用户不确定该行的用处或者不知道填什么内容时,可以作为input里placeh 核心板:嵌入式系统的核心驱动力 MYZR1 核心板人工智能SSD2351 核心板(CoreBoard)作为嵌入式系统开发的核心组件,已成为现代电子设备智能化的重要基石。这种高度集成的电路板将处理器、内存、存储和基本外设接口浓缩在一个紧凑的模块中,为各类智能设备提供强大的"大脑"。核心板的技术特点核心板通常采用先进的系统级封装(SiP)技术,在微小空间内集成了CPU/GPU、DDR内存、Flash存储以及电源管理单元。这种设计不仅大幅减小了体积,还提高了系统可靠性。以常见 智能网关:物联网时代的核心枢纽 MYZR1 物联网人工智能核心板SSD2351 随着物联网技术的快速发展,智能网关作为连接物理世界与数字世界的桥梁,正发挥着越来越重要的作用。智能网关不仅是一个简单的数据传输节点,更是实现设备互联、协议转换、边缘计算的关键组件,为智慧家庭、工业物联网、智慧城市等应用场景提供了基础支撑。智能网关的核心功能智能网关的首要任务是解决不同设备间的通信协议差异问题。在物联网环境中,各类传感器、终端设备可能采用Zigbee、蓝牙、Wi-Fi、LoRa等不同 TOMCAT在POST方法提交参数丢失问题 357029540 javatomcatjsp 摘自http://my.oschina.net/luckyi/blog/213209 昨天在解决一个BUG时发现一个奇怪的问题,一个AJAX提交数据在之前都是木有问题的,突然提交出错影响其他处理流程。 检查时发现页面处理数据较多,起初以为是提交顺序不正确修改后发现不是由此问题引起。于是删除掉一部分数据进行提交,较少数据能够提交成功。 恢复较多数据后跟踪提交FORM DATA ,发现数 在MyEclipse中增加JSP模板 删除-2008-08-18 ljy325 jspxmlMyEclipse 在D:\Program Files\MyEclipse 6.0\myeclipse\eclipse\plugins\com.genuitec.eclipse.wizards_6.0.1.zmyeclipse601200710\templates\jsp 目录下找到Jsp.vtl,复制一份,重命名为jsp2.vtl,然后把里面的内容修改为自己想要的格式,保存。 然后在 D:\Progr JavaScript常用验证脚本总结 eksliang JavaScriptjavaScript表单验证 转载请出自出处:http://eksliang.iteye.com/blog/2098985 下面这些验证脚本,是我在这几年开发中的总结,今天把他放出来,也算是一种分享吧,现在在我的项目中也在用!包括日期验证、比较,非空验证、身份证验证、数值验证、Email验证、电话验证等等...! &nb 微软BI(4) 18289753290 微软BI SSIS 1) Q:查看ssis里面某个控件输出的结果: A MessageBox.Show(Dts.Variables["v_lastTimestamp"].Value.ToString()); 这是我们在包里面定义的变量 2):在关联目的端表的时候如果是一对多的关系,一定要选择唯一的那个键作为关联字段。 3) Q:ssis里面如果将多个数据源的数据插入目的端一 定时对大数据量的表进行分表对数据备份 酷的飞上天空 大数据量 工作中遇到数据库中一个表的数据量比较大,属于日志表。正常情况下是不会有查询操作的,但如果不进行分表数据太多,执行一条简单sql语句要等好几分钟。。 分表工具:linux的shell + mysql自身提供的管理命令 原理:使用一个和原表数据结构一样的表,替换原表。 linux shell内容如下: =======================开始  本质的描述与因材施教 永夜-极光 感想随笔 不管碰到什么事,我都下意识的想去探索本质,找寻一个最形象的描述方式。 我坚信,世界上对一件事物的描述和解释,肯定有一种最形象,最贴近本质,最容易让人理解 & 很迷茫。。。 随便小屋 随笔 小弟我今年研一,也是从事的咱们现在最流行的专业(计算机)。本科三流学校,为了能有个更好的跳板,进入了考研大军,非常有幸能进入研究生的行业(具体学校就不说了,怕把学校的名誉给损了)。 先说一下自身的条件,本科专业软件工程。主要学习就是软件开发,几乎和计算机没有什么区别。因为学校本身三流,也就是让老师带着学生学点东西,然后让学生毕业就行了。对专业性的东西了解的非常浅。就那学的语言来说 23种设计模式的意图和适用范围 aijuans 设计模式 Factory Method 意图 定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。 适用性 当一个类不知道它所必须创建的对象的类的时候。 当一个类希望由它的子类来指定它所创建的对象的时候。 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。 Abstr Java中的synchronized和volatile aoyouzi javavolatilesynchronized 说到Java的线程同步问题肯定要说到两个关键字synchronized和volatile。说到这两个关键字,又要说道JVM的内存模型。JVM里内存分为main memory和working memory。 Main memory是所有线程共享的,working memory则是线程的工作内存,它保存有部分main memory变量的拷贝,对这些变量的更新直接发生在working memo js数组的操作和this关键字 百合不是茶 js数组操作this关键字 js数组的操作; 一:数组的创建: 1、数组的创建 var array = new Array(); //创建一个数组 var array = new Array([size]); //创建一个数组并指定长度,注意不是上限,是长度 var arrayObj = new Array([element0[, element1[, ...[, elementN]]] 别人的阿里面试感悟 bijian1013 面试分享工作感悟阿里面试 原文如下:http://greemranqq.iteye.com/blog/2007170 一直做企业系统,虽然也自己一直学习技术,但是感觉还是有所欠缺,准备花几个月的时间,把互联网的东西,以及一些基础更加的深入透析,结果这次比较意外,有点突然,下面分享一下感受吧! &nb 淘宝的测试框架Itest Bill_chen springmaven框架单元测试JUnit Itest测试框架是TaoBao测试部门开发的一套单元测试框架,以Junit4为核心, 集合DbUnit、Unitils等主流测试框架,应该算是比较好用的了。 近期项目中用了下,有关itest的具体使用如下: 1.在Maven中引入itest框架: <dependency> <groupId>com.taobao.test</groupId&g 【Java多线程二】多路条件解决生产者消费者问题 bit1129 java多线程 package com.tom; import java.util.LinkedList; import java.util.Queue; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.locks.Condition; import java.util.concurrent.loc 汉字转拼音pinyin4j 白糖_ pinyin4j 以前在项目中遇到汉字转拼音的情况,于是在网上找到了pinyin4j这个工具包,非常有用,别的不说了,直接下代码: import java.util.HashSet; import java.util.Set; import net.sourceforge.pinyin4j.PinyinHelper; import net.sourceforge.pinyin org.hibernate.TransactionException: JDBC begin failed解决方案 bozch ssh数据库异常DBCP org.hibernate.TransactionException: JDBC begin failed: at org.hibernate.transaction.JDBCTransaction.begin(JDBCTransaction.java:68) at org.hibernate.impl.SessionImp java-并查集(Disjoint-set)-将多个集合合并成没有交集的集合 bylijinnan java import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.ut Java PrintWriter打印乱码 chenbowen00 java 一个小程序读写文件,发现PrintWriter输出后文件存在乱码,解决办法主要统一输入输出流编码格式。 读文件: BufferedReader 从字符输入流中读取文本,缓冲各个字符,从而提供字符、数组和行的高效读取。 可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。 通常,Reader 所作的每个读取请求都会导致对基础字符或字节流进行相应的读取请求。因 [天气与气候]极端气候环境 comsci 环境 如果空间环境出现异变...外星文明并未出现,而只是用某种气象武器对地球的气候系统进行攻击,并挑唆地球国家间的战争,经过一段时间的准备...最大限度的削弱地球文明的整体力量,然后再进行入侵...... 那么地球上的国家应该做什么样的防备工作呢? &n oracle order by与union一起使用的用法 daizj UNIONoracleorder by 当使用union操作时,排序语句必须放在最后面才正确,如下: 只能在union的最后一个子查询中使用order by,而这个order by是针对整个unioning后的结果集的。So: 如果unoin的几个子查询列名不同,如 Sql代码 select supplier_id, supplier_name from suppliers UNI zeus持久层读写分离单元测试 deng520159 单元测试 本文是zeus读写分离单元测试,距离分库分表,只有一步了.上代码: 1.ZeusMasterSlaveTest.java package com.dengliang.zeus.webdemo.test; import java.util.ArrayList; import java.util.List; import org.junit.Assert; import org.j Yii 截取字符串(UTF-8) 使用组件 dcj3sjt126com yii 1.将Helper.php放进protected\components文件夹下。 2.调用方法: Helper::truncate_utf8_string($content,20,false); //不显示省略号 Helper::truncate_utf8_string($content,20); //显示省略号 &n 安装memcache及php扩展 dcj3sjt126com PHP 安装memcache tar zxvf memcache-2.2.5.tgz cd memcache-2.2.5/ /usr/local/php/bin/phpize (?) ./configure --with-php-confi JsonObject 处理日期 feifeilinlin521 javajsonJsonOjbectJsonArrayJSONException 写这边文章的初衷就是遇到了json在转换日期格式出现了异常 net.sf.json.JSONException: java.lang.reflect.InvocationTargetException 原因是当你用Map接收数据库返回了java.sql.Date 日期的数据进行json转换出的问题话不多说 直接上代码 &n Ehcache(06)——监听器 234390216 监听器listenerehcache 监听器 Ehcache中监听器有两种,监听CacheManager的CacheManagerEventListener和监听Cache的CacheEventListener。在Ehcache中,Listener是通过对应的监听器工厂来生产和发生作用的。下面我们将来介绍一下这两种类型的监听器。 activiti 自带设计器中chrome 34版本不能打开bug的解决 jackyrong Activiti 在acitivti modeler中,如果是chrome 34,则不能打开该设计器,其他浏览器可以, 经证实为bug,参考 http://forums.activiti.org/content/activiti-modeler-doesnt-work-chrome-v34 修改为,找到 oryx.debug.js 在最头部增加 if (!Document. 微信收货地址共享接口-终极解决 laotu5i0 微信开发 最近要接入微信的收货地址共享接口,总是不成功,折腾了好几天,实在没办法网上搜到的帖子也是骂声一片。我把我碰到并解决问题的过程分享出来,希望能给微信的接口文档起到一个辅助作用,让后面进来的开发者能快速的接入,而不需要像我们一样苦逼的浪费好几天,甚至一周的青春。各种羞辱、谩骂的话就不说了,本人还算文明。 如果你能搜到本贴,说明你已经碰到了各种 ed 关于人才 netkiller.github.com 工作面试招聘netkiller人才 关于人才 每个月我都会接到许多猎头的电话,有些猎头比较专业,但绝大多数在我看来与猎头二字还是有很大差距的。 与猎头接触多了,自然也了解了他们的工作,包括操作手法,总体上国内的猎头行业还处在初级阶段。 总结就是“盲目推荐,以量取胜”。 目前现状 许多从事人力资源工作的人,根本不懂得怎么找人才。处在人才找不到企业,企业找不到人才的尴尬处境。 企业招聘,通常是需要用人的部门提出招聘条件,由人 搭建 CentOS 6 服务器 - 目录 rensanning centos (1) 安装CentOS ISO(desktop/minimal)、Cloud(AWS/阿里云)、Virtualization(VMWare、VirtualBox) 详细内容 (2) Linux常用命令 cd、ls、rm、chmod...... 详细内容 (3) 初始环境设置 用户管理、网络设置、安全设置...... 详细内容 (4) 常驻服务Daemon 【求助】mongoDB无法更新主键 toknowme mongodb Query query = new Query(); query.addCriteria(new Criteria("_id").is(o.getId())); &n jquery 页面滚动到底部自动加载插件集合 xp9802 jquery 很多社交网站都使用无限滚动的翻页技术来提高用户体验,当你页面滑到列表底部时候无需点击就自动加载更多的内容。下面为你推荐 10 个 jQuery 的无限滚动的插件: 1. jQuery ScrollPagination jQuery ScrollPagination plugin 是一个 jQuery 实现的支持无限滚动加载数据的插件。 2. jQuery Screw S 按字母分类: ABCDEFGHIJKLMNOPQRSTUVWXYZ其他
因为 Vue 只有在浏览器解析和标准化 HTML 后才能获取模版内容。尤其像这些元素
<table> <my-row>...my-row> table> <table> <tr is="my-row">tr> table>
使用组件时,大多数可以传入到 Vue 构造器中的选项可以在注册组件时使用,有一个例外: data 必须是函数。 实际上
//这样会报错,提示data必须是一个函数 Vue.component('my-component', { template: '{{ message }}', data: { message: 'hello' } })
<div id="example-2"> <simple-counter>simple-counter> <simple-counter>simple-counter> <simple-counter>simple-counter> div>
var data = { counter: 0 } Vue.component('simple-counter', { template: '{{ counter }}', // data 是一个函数,因此 Vue 不会警告, // 但是我们为每一个组件返回了同一个对象引用,所以改变其中一个会把其他都改变了 data: function () { return data } }) new Vue({ el: '#example-2' })
避免出现同时改变数据的情况
//返回一个新的对象,而不是返回同一个data对象引用 data: function () { return { //字面量写法会创建新对象 counter: 0 } }
组件意味着协同工作,通常父子组件会是这样的关系:
组件 A 在它的模版中使用了组件 B 。它们之间必然需要相互通信
父组件要给子组件传递数据,子组件需要将它内部发生的事情告知给父组件
然而,在一个良好定义的接口中尽可能将父子组件解耦是很重要的。这保证了每个组件可以在相对隔离的环境中书写和理解,也大幅提高了组件的可维护性和可重用性。
在 Vue.js 中,父子组件的关系可以总结为 props down, events up 。 父组件通过 props 向下传递数据给子组件,子组件通过 events 给父组件发送消息。看看它们是怎么工作的。
使用prop传递数据
组件实例的作用域是孤立的。这意味着不能并且不应该在子组件的模板内直接引用父组件的数据。
使用 props 把数据传给子组件。
prop 是父组件用来传递数据的一个自定义属性
子组件需要显式地用 props 选项声明 “prop”
<div id="example-2"> <child message="hello!">child> div>
Vue.component('child', { // 声明 props,用数组形式的对象 props: ['message'], // 就像 data 一样,prop 可以用在模板内 // 同样也可以在 vm 实例中像 “this.message” 这样使用 template: '{{ message }}' }); new Vue({ el: '#example-2' })
用 v-bind 动态绑定 props 的值到父组件的数据中。每当父组件的数据变化时,该变化也会传导给子组件
<div id="example-2"> <input v-model="parentMsg"> <br> <child v-bind:my-message="parentMsg">child> div>
Vue.component('child', { // 声明 props props: ['my-message'], template: '{{ myMessage }}' //如果写my-message会报错,需要转换为驼峰写法 }); new Vue({ el: '#example-2', data: { parentMsg: '' } })
HTML 特性不区分大小写。当使用非字符串模版时,prop的名字形式会从 camelCase 转为 kebab-case(短横线隔开)
在JavaScript里面使用驼峰写法,但是在html里面需要转成短横线写法
反之亦然,vue会自动处理来自html的短横线写法转为驼峰写法
<comp some-prop="1">comp> <comp v-bind:some-prop="1">comp>
prop 是单向绑定的
当父组件的属性变化时,将传导给子组件,但是不会反过来。这是为了防止子组件无意修改了父组件的状态——这会让应用的数据流难以理解。
每次父组件更新时,子组件的所有 prop 都会更新为最新值。这意味着你不应该在子组件内部改变 prop 。如果你这么做了,Vue 会在控制台给出警告。
通常有两种改变 prop 的情况:
prop 作为初始值传入,子组件之后只是将它的初始值作为本地数据的初始值使用
定义一个局部 data 属性,并将 prop 的初始值作为局部数据的初始值。
<div id="example-2"> <child initial-counter="10">child> div>
Vue.component('child', { props: ['initialCounter'],//这里用驼峰写法 data: function () { //转为一个局部变量,写一个data对象给组件使用 return {counter: this.initialCounter} }, template: '{{ counter }}' }); new Vue({ el: '#example-2' })
prop 作为需要被转变的原始值传入。
定义一个 computed 属性,此属性从 prop 的值计算得出。
//例子没有写完,但是根据第一个例子可以知道利用computed的手法原理其实跟写一个data差不多 props: ['size'], computed: { normalizedSize: function () { return this.size.trim().toLowerCase() } }
注意在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,如果 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态。
组件可以为 props 指定验证要求,当组件给其他人使用时这很有用。
Vue.component('example', { props: { // 基础类型检测 (`null` 意思是任何类型都可以) 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 value > 10 } } } })
每个 Vue 实例都实现了事件接口(Events interface)
使用 $on(eventName) 监听事件
使用 $emit(eventName) 触发事件
父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件。
<div id="counter-event-example"> <p>{{ total }}p> <button-counter v-on:increment1="incrementTotal">button-counter> <button-counter v-on:increment1="incrementTotal">button-counter> div>
Vue.component('button-counter', { //监听click事件,处理程序为increment(子组件定义的方法) template: '{{ counter }}', //每一个counter都是独立的对象属性 data: function () { return { counter: 0 } }, //子组件的方法 methods: { increment: function () { this.counter += 1; //在子组件里面直接触发之前监听的increment1事件来执行父组件的方法 this.$emit('increment1'); } }, }) new Vue({ el: '#counter-event-example', data: { total: 0 }, //父组件的方法 methods: { incrementTotal: function () { this.total += 1 } } })
1.组件之间因为作用域不同的关系,所以相互独立,所以子组件想要使用父组件的方法的话需要做一个新的监听映射
<my-component v-on:click.native="doTheThing">my-component>
自定义事件也可以用来创建自定义的表单输入组件,使用 v-model 来进行数据双向绑定。所以要让组件的 v-model 生效,它必须:
接受一个 value 属性
在有新的 value 时触发 input 事件
<input v-model="something"> <input v-bind:value="something" v-on:input="something = $event.target.value">
一个非常简单的货币输入:
<currency-input v-model="price">currency-input>
Vue.component('currency-input', { template: '\ \ $\ \ \ ', props: ['value'], //父组件将绑定的value传给子组件 methods: { // 不是直接更新值,而是使用此方法来对输入值进行格式化和位数限制 updateValue: function (value) { var formattedValue = value //对值进行处理 // 删除两侧的空格符 .trim() // 保留 2 小数位和2位数 .slice(0, value.indexOf('.') + 3) // 如果值不统一,手动覆盖以保持一致,为了保持输入框显示内容跟格式化内容一致 if (formattedValue !== value) { //因为注册是一个input元素,所以this.$refs 就是input元素 this.$refs.input.value = formattedValue } //手动触发input事件,将格式化后的值传过去,这是最终显示输入框的输出 this.$emit('input', Number(formattedValue)) } } }) //实例化vue实例的 new Vue({ el: '#aa', //要绑定一个vue实例,例如包裹一个id为aa的div data:{ price:'' //v-model要有数据源 } })
ref 被用来给元素或子组件注册引用信息。引用信息会根据父组件的 $refs 对象进行注册。如果在普通的DOM元素上使用,引用信息就是元素; 如果用在子组件上,引用信息就是组件实例 ref
这是一个比较完整的例子:
<div id="app"> <currency-input label="Price" v-model="price" >currency-input> <currency-input label="Shipping" v-model="shipping" >currency-input> <currency-input label="Handling" v-model="handling" >currency-input> <currency-input label="Discount" v-model="discount" >currency-input> <p>Total: ${{ total }}p> div>
Vue.component('currency-input', { template: '\ \ {{ label }}\ $\ \ \ ', props: { //多个prop传递,因为prop是对象,只要是对象格式就行 value: { type: Number, default: 0 }, label: { type: String, default: '' } }, mounted: function () { //这是vue的过渡状态,暂时忽略不影响理解 this.formatValue() }, methods: { updateValue: function (value) { var result = currencyValidator.parse(value, this.value) if (result.warning) { // 这里也使用了$refs获取引用注册信息 this.$refs.input.value = result.value } this.$emit('input', result.value) }, formatValue: function () { this.$refs.input.value = currencyValidator.format(this.value) //这里注意下,这个this是prop传递过来的,也相当于这个组件作用域 }, selectAll: function (event) { //event可以获取原生的js事件 // Workaround for Safari bug // http://stackoverflow.com/questions/1269722/selecting-text-on-focus-using-jquery-not-working-in-safari-and-chrome setTimeout(function () { event.target.select() }, 0) } } }) new Vue({ el: '#app', data: { price: 0, shipping: 0, handling: 0, discount: 0 }, computed: { total: function () { return (( this.price * 100 + this.shipping * 100 + this.handling * 100 - this.discount * 100 ) / 100).toFixed(2) } } })
在简单的场景下,使用一个空的 Vue 实例作为中央事件总线:
var bus = new Vue() // 触发组件 A 中的事件 bus.$emit('id-selected', 1) /* 通过on来监听子组件的事件来实现传递 */ // 在组件 B 创建的钩子中监听事件 bus.$on('id-selected', function (id) { // ... })
为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板。这个过程被称为 内容分发 (或 “transclusion” 如果你熟悉 Angular)
组件作用域简单地说是:父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译。假定 someChildProperty 是子组件的属性,上例不会如预期那样工作。父组件模板不应该知道子组件的状态。
<child-component v-show="someChildProperty">child-component>
如果要绑定子组件内的指令到一个组件的根节点,应当在它的模板内这么做:
Vue.component('child-component', { // 有效,因为是在正确的作用域内 template: 'Child', data: function () { return { //因为这个属性在当前组件内编译(创建了) someChildProperty: true } } })
类似地,分发内容是在父组件作用域内编译。
除非子组件模板包含至少一个 插口,否则父组件的内容将会被丢弃。
当子组件模板只有一个没有属性的 slot 时,父组件整个内容片段将插入到 slot 所在的 DOM 位置,并替换掉 slot 标签本身。
备用内容在子组件的作用域内编译,并且只有在宿主元素为空,且没有要插入的内容时才显示备用内容。
<div id="aa"> <h1>我是父组件的标题h1> <my-component>my-component> <my-component> <p>这是一些初始内容p> <p>这是更多的初始内容p> my-component> div>
Vue.component('my-component', { //my-component 组件有下面模板 template: '\ \ 我是子组件的标题 \ \ //有slot插口,所以没有被父组件丢弃 只有在没有要分发的内容时才会显示。\ \ \ ' }) new Vue({ el: '#aa', })
渲染结果:
<div id="aa"><h1>我是父组件的标题h1> <div> <h2>我是子组件的标题h2> 只有在没有要分发的内容时才会显示。 div> <div> <h2>我是子组件的标题h2> <p>这是一些初始内容p> <p>这是更多的初始内容p> div> div>
元素可以用一个特殊的属性 name 来配置如何分发内容。多个 slot 可以有不同的名字。具名 slot 将匹配内容片段中有对应 slot 特性的元素。
仍然可以有一个匿名 slot ,它是默认 slot ,作为找不到匹配的内容片段的备用插槽。如果没有默认的 slot ,这些找不到匹配的内容片段将被抛弃。
<div id="aa"> <app-layout> <h1 slot="header">这里可能是一个页面标题h1> <p>主要内容的一个段落。p> <p>另一个主要段落。p> <p slot="footer">这里有一些联系信息p> app-layout> div>
Vue.component('app-layout', { template: '\ \ \ //找到名字叫header的slot之后替换内容,这里替换的是整个DOM \ \ \ //因为slot没有属性,会将内容插入到slot的所在的DOM位置 \ \ \ \ ' }); new Vue({ el: '#aa', })
渲染结果为:
<div class="container"> <header> <h1>这里可能是一个页面标题h1> header> <main> <p>主要内容的一个段落。p> <p>另一个主要段落。p> main> <footer> <p>这里有一些联系信息p> footer> div>
作用域插槽是一种特殊类型的插槽,用作使用一个(能够传递数据到)可重用模板替换已渲染元素。
在子组件中,只需将数据传递到插槽,就像你将 prop 传递给组件一样
在父级中,具有特殊属性 scope 的 元素,表示它是作用域插槽的模板。scope 的值对应一个临时变量名,此变量接收从子组件中传递的 prop 对象
<div id="parent" class="parent"> <child> <template scope="props"> <span>hello from parentspan> <span>{{ props.text }}span> template> child> div>
Vue.component('child', { props: ['props'], //这个写不写都可以,作用域插槽固定会接收prop对象,而且这个prop对象是肯定存在的 template: '\ \ \ //在子组件里直接将数据传递给slot \ ' }); new Vue({ el: '#parent', })
渲染结果:
<div class="parent"> <div class="child"> <span>hello from parentspan> <span>hello from childspan> div> div>
另外一个例子,作用域插槽更具代表性的用例是列表组件,允许组件自定义应该如何渲染列表每一项
<div id="parent"> <my-awesome-list :items="items"> <template slot="item" scope="props"> <li class="my-fancy-item">{{ props.text }}li> template> my-awesome-list> div>
Vue.component('my-awesome-list', { props:['items'], //需要声明prop为items,需要是为下面的循环遍历的items的数据源做设定,位置3 template: '\ \ \ //在slot中,循环遍历输出items的text,位置4 \ \ ' }); new Vue({ el: '#parent', data : { items:[ //初始化items数据 {text:"aa"}, {text:"bb"} ] } })
位置1,实现了一个组件的prop绑定,prop需要在组件里面声明,这里绑定的是items,这是要将父组件的items传递到子组件,所以在位置3里面需要声明,在vue实例要初始化
位置2,这里scope的props是代表作用域插槽接收来自prop对象的数据,props.text是代表每一个li要输出的是prop对象的text属性
位置3,在组件里声明props,为了接收父组件绑定的items属性,然后将其给位置4的循环使用
位置4,这里绑定了text属性,就是前呼位置2里面输出的prop对象的text属性
多个组件可以使用同一个挂载点,然后动态地在它们之间切换。使用保留的 元素,动态地绑定到它的 is 特性
var vm = new Vue({ el: '#example', data: { currentView: 'home' //默认值 }, components: { //根据不同的值进行不同的组件切换,这里用components写法 home: { /* ... */ }, posts: { /* ... */ }, archive: { /* ... */ } } })
<component v-bind:is="currentView"> component>
如果把切换出去的组件保留在内存中,可以保留它的状态或避免重新渲染。为此可以添加一个 keep-alive 指令参数
<keep-alive> <component :is="currentView"> component> keep-alive>
在编写组件时,记住是否要复用组件有好处。一次性组件跟其它组件紧密耦合没关系,但是可复用组件应当定义一个清晰的公开接口。Vue 组件的 API 来自三部分 - props, events 和 slots :
Props 允许外部环境传递数据给组件
Events 允许组件触发外部环境的副作用
Slots 允许外部环境将额外的内容组合在组件中。
<my-component :foo="baz" :bar="qux" @event-a="doThis" @event-b="doThat" > <img slot="icon" src="..."> <p slot="main-text">Hello!p> my-component>
尽管有 props 和 events ,但是有时仍然需要在 JavaScript 中直接访问子组件。为此可以使用 ref 为子组件指定一个索引 ID 。
<div id="parent"> <user-profile ref="profile">user-profile> div>
var parent = new Vue({ el: '#parent' }) // 访问子组件 var child = parent.$refs.profile
当 ref 和 v-for 一起使用时, ref 是一个数组或对象,包含相应的子组件。 $refs 只在组件渲染完成后才填充,并且它是非响应式的。它仅仅作为一个直接访问子组件的应急方案——应当避免在模版或计算属性中使用 $refs 。 ref 被用来给元素或子组件注册引用信息。引用信息会根据父组件的 $refs 对象进行注册。如果在普通的DOM元素上使用,引用信息就是元素; 如果用在子组件上,引用信息就是组件实例 ref
当 ref 和 v-for 一起使用时, ref 是一个数组或对象,包含相应的子组件。
$refs 只在组件渲染完成后才填充,并且它是非响应式的。它仅仅作为一个直接访问子组件的应急方案——应当避免在模版或计算属性中使用 $refs 。
当注册组件(或者 props)时,可以使用 kebab-case ,camelCase ,或 TitleCase 。Vue 不关心这个。
在 HTML 模版中,请使用 kebab-case 形式:
// 在组件定义中 components: { // 使用 kebab-case 形式注册--横线写法 'kebab-cased-component': { /* ... */ }, // register using camelCase --驼峰写法 'camelCasedComponent': { /* ... */ }, // register using TitleCase --标题写法 'TitleCasedComponent': { /* ... */ } }
<kebab-cased-component>kebab-cased-component> <camel-cased-component>camel-cased-component> <title-cased-component>title-cased-component>
组件在它的模板内可以递归地调用自己,不过,只有当它有 name 选项时才可以
当你利用Vue.component全局注册了一个组件, 全局的ID作为组件的 name 选项,被自动设置.
//组件可以用name来写名字 name: 'unique-name-of-my-component' //也可以在创建的时候默认添加名字 Vue.component('unique-name-of-my-component', { // ... }) //如果同时使用的话,递归的时候就会不断递归自己,导致溢出 name: 'stack-overflow', template: ''
尽管在 Vue 中渲染 HTML 很快,不过当组件中包含大量静态内容时,可以考虑使用 v-once 将渲染结果缓存起来,就像这样:
Vue.component('terms-of-service', { template: '\ \ Terms of Service\ ... a lot of static content ...\ \ ' })
v-once只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。