关于component:关于类的加载和static变量及方法和构造器运行

public class Demo2 { public static int k=0;public static Demo2 t1=new Demo2("t1");public static Demo2 t2=new Demo2("t2");public static int i=print("i");public static int j=print("j");public static int n=99;//代码块{ print("构造函数");}//动态代码块static{ print("动态代码块"); }//静态方法public static int print(String s){ System.out.println(" i= " + i + " " + s + " k= " + k + " n= " + n + " j = " + j); ++i; ++k; ++n; return i;}//结构器public Demo2(String string){ print(string);} //主函数public static void main(String[] args){ Demo2 d=new Demo2 d=new Demo2("T");}} ...

November 11, 2021 · 1 min · jiezi

Spring-讲解四

Spring 中使用注解注入注解:就是一个类,使用 @ 注解名称。实际开发中:使用注解取代 xml 配置文件。 1、常用注解释义 @component 取代 <bean class="">@Component("id") 取代 <bean id="" class="">web开发,提供3个 @Component 注解衍生注解取代<bean class=""> @Repository(“名称”):dao层@Service(“名称”):service层 @Controller(“名称”):web层 web 开发中其他常用注解 @Autowired:自动根据类型注入@Qualifier(“名称”):指定自动注入的id名称 @Resource(“名称”) @ PostConstruct 自定义初始化 @ PreDestroy 自定义销毁 2、案例代码演示 接口和实现类public interface UserService { void add(User user);}=========================================================================================@Componentpublic class UserServiceImpl implements UserService { @Override public void add(User user) { System.out.println("添加用户信息..."+user); }}配置 bean.xml 文件<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 开启注解--> <context:annotation-config/> <!-- 注解的位置--> <context:component-scan base-package="com.example.demo"/></beans>测试函数public class ServiceTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); UserService userService = context.getBean(UserServiceImpl.class); User user = new User(); user.setUsername("元始天尊"); user.setAge(999); user.setPassword("555"); userService.add(user); }}控制台日志信息如下:添加用户信息...User{username='元始天尊', password='555', age=999} ...

November 5, 2019 · 1 min · jiezi

react之如何写一个管理自有状态的自定义组件

一、函数组件函数组件类似一个纯函数,接受外部传入的参数,生成并返回一个React元素(伪DOM)。例如,如下,Greeting作为一个组件,接受传入的参数name,并返回一个内容已填充的p标签。 function Greeting (props) { return ( <p> {props.name},how are you? </p> )}const element = <Greeting name="Agnes" />ReactDOM.render( element, document.getElementById('root'))二、class组件react中class组件的书写方式跟es6中类的书写方式非常接近,可以通过React.Compnent进行创建。与函数组件不同的是,该组件可以进行复杂逻辑的处理,既可以接受外部参数,也可以拥有自己的state,用于组件内的通信。 class HighGreeting extends React.Component { constructor(props){ super(props); this.state={ inputValue: this.props.name } this.handleInputChange = this.handleInputChange.bind(this); } render () { return ( <input type="text" onChange="handleInputChange"/> <p>{this.state.inputValue},how are you?</p> ) } handleInputChange(e){ let value = e.target.value; this.setState({ inputValue: value }) }} const element = <HighGreeting name="Agnes" />ReactDOM.render( element, document.getElementById('root'))上面的组件,接收props参数作为初始值,当用户输入时,会实时更新。 每次定义子类的构造函数时,都需要调用super方法,因此所有含有构造函数的React组件中,构造函数必须以super开头。调用super相当于初始化this.props。class组件内部可以定义state,相当于vue组件内的data,更改时需要调用this.setState,每次调用该方法时,都会执行render方法,自动更新组件。如上图,监听input的onchange事件,实时更改inputValue的值并展示。需要注意的是,props不仅可以传递值,还可以传递函数,甚至传递另一个组件。(这一点跟vue不一样,后面的文章会再细讲。)

July 10, 2019 · 1 min · jiezi

Vue动态表单-vuedynamicform2

背景前不久,我在github上开源了一个业务工作中沉淀出来的工具 app-info-parser ,然后就有了半夜三四点回复issue邮件、修bug的爽歪歪体验,虽然上完班还要处理issue挺累的,但是也算乐在其中。 俗话说得好,开源一时爽,一直开源一直爽,所以我又来了 -。- 我所在的小组主要负责公司的公共服务搭建,组内有不下十个公共服务项目(就我一个前端,太过分了T_T),每个项目都有配套的 CURD 类的管理系统,随之而来的就是数不清的表单。 不夸张的说,我写表单都快写吐了。作为一个程序员,实在无法容忍自己把时间花在重复的事情上,所以抽时间在 element-ui 和 async-validator 的基础上写了一个动态表单组件 vue-dynamic-form 安利正式开始安装NPM 包引入,vue-dynamic-form名字被占用了(哭瞎),因此在 NPM 中使用的名字是 vue-dynamic-form2 yarn add vue-dynamic-form2# npmnpm install vue-dynamic-form2script 标签引入请自行在 github仓库 中下载对应版本的源码,引入 lib/vue-dynamic-form.umd.min.js注册全局注册 import Vue from 'Vue'import DynamicForm from 'vue-dynamic-form2'Vue.use(DynamicForm)组件内注册 <script>import DynamicForm from 'vue-dynamic-form2'export default { components: { DynamicForm }}</script>简单示例<template> <dynamic-form v-model="data" :descriptors="descriptors"> </dynamic-form></template><script>export default { data () { return { descriptors: { date: { type: 'date', label: 'date \'s label', required: false }, number: { type: 'number', label: 'number \'s label', required: true, placeholder: 'please input the number' }, string: { type: 'string', label: 'string \'s label', required: true, pattern: /^test$/g }, url: { type: 'url', label: 'url \'s label', required: true, placeholder: 'please input the url' }, email: { type: 'email', label: 'email \'s label', required: false }, enum: { type: 'enum', label: 'enum\'s label', enum: ['value-1', 'value-2'] } }, data: {} } }}</script>生成的表单 ...

June 19, 2019 · 2 min · jiezi

Vue-组件通信详解

父子组件通信: props、 $parent / $children、 provide / inject 、 ref 、 $attrs / $listeners兄弟组件通信:EventBus 、 Vuex跨级组件通信: EventBus 、 Vuex 、 provide / inject 、 $attrs / $listeners父传子 子组件用 props 接收,父组件用 v-bind:prop 发送父组件<template> <div class="section"> <com-article :articles="articleList"></com-article> </div></template><script>import comArticle from "./comArticle";export default { data() { return { articleList: ["红楼梦", "西游记", "三国演义", "水浒传"] } }, components: { comArticle },}</script>子组件<template> <ul> <li v-for="(item, index) in articles" :key="index">{{item}}</li> </ul></template><script>export default { props: ["articles"]}</script>子传父 子组件用 v-on:click="" this.$emit('name', this.msg)(【有的版本名称只能小写】)发送,父组件自定义事件 v-on:name="getChildValue" 然后在 getChildValue(data){} 方法中接收父组件<template> <div class="section"> <com-article @onEmitIndex="onEmitIndex"></com-article> 【不能加括号】 <ul> <li v-for="(item, index) in articles" :key="index">{{item}}</li> </ul> </div></template><script>import comArticle from "./com2";export default { data() { return { articles:[] }; }, components: { comArticle }, methods: { onEmitIndex(data) { this.articles = data; } }}</script>子组件<template> <div> <button @click="emitIndex()">点击把articleList传给父组件</button> 【可以传参】 </div></template><script>export default { data() { return { articleList: ["红楼梦", "西游记", "三国演义", "水浒传"] }; }, methods: { emitIndex() { this.$emit("onEmitIndex", this.articleList); // } }}</script>父子传参还可以用 $parent(对象)和 $children(数组)provide / reject (上传下)父辈组件中通过 provide 来提供变量,子孙组件中通过 reject 来注入变量。父组件<template> <div> com1 是父组件 <com2></com2> </div></template><script> import com2 from './com2.vue' export default { provide: { msg: "这是父辈组件 com1 传出去的数据" }, components:{ com2 } }</script>子组件<template> <div> com2 是 com1 的子组件 {{demo}} <com3></com3> </div></template><script> import com3 from './com3.vue' export default { inject: ['msg'], data() { return { demo: this.msg } }, components: { com3 } }</script>孙组件<template> <div> com3 是 com1 的孙组件 {{msg}} </div></template><script> export default { inject: ['msg'] }</script>ref如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例,可以通过实例直接调用组件的方法或访问数据 ref="xx" this.$refs.xxeventBus(事件总线,项目较大难以维护,组件都可以传) $emit(name, data)发送 $on(name, data=>{})接收 【名称小写】event-bus.jsimport Vue from 'vue'export const EventBus = new Vue()com1.vue 发送事件<button @click="additionHandle">加法器</button>import {EventBus} from './event-bus.js'data(){ return {num: 1}},additionHandle(){ EventBus.$emit('addition', {num: this.num++})com2.vue 接收事件<div>计算和: {{count}}</div>data() { return {count: 0}},mounted() { EventBus.$on('addition', param => { this.count = this.count + param.num; })}localStorage / sessionStorage因为 window.loacalStorage.setItem(key, value)、window.loacalStorage.getItem(key) 储存的是字符串,需要用 JSON.parse() / stringify() 转换可结合 vuex,实现数据持久保存和解决数据及状态混乱问题$attrs $listeners(仅仅是传递数据,而不做中间处理,使用 vuex 处理,未免有点大材小用)test.vue<template> <div> test.vue <child-com1 :name="name" :age="age" :gender="gender" :height="height" title="test.vue 传出的值"></child-com1> </div></template><script>const childCom1 = () => import("./com1.vue");export default { components: { childCom1 }, data() { return { name: "zhangsan", age: "18", gender: "女", height: "158" }; }};</script><style scoped>div{ background-color: #ddd;}</style>com1.vue<template> <div class="com1"> com1 <p>name: {{name}}</p> <p>childCom1的$attrs: {{$attrs}}</p> <child-com2 v-bind="$attrs"></child-com2> </div></template><script>const childCom2 = () => import("./com2.vue");export default { components: { childCom2 }, inheritAttrs: false, // 关闭自动挂载到组件根元素上的没有在 props 声明的属性 props: { name: String }, created() { console.log(this.$attrs); // {age: "18", gender: "女", height: "158", title: "test.vue 传 com1.vue"} }};</script><style scoped>.com1{ margin: 20px; background-color: #f00;}</style>com2.vue<template> <div>com2 <p>age: {{age}}</p> <p>childCom2: {{ $attrs }}</p> </div></template><script>export default { inheritAttrs: false, props: { age: String }, created() { console.log('com2', this.$attrs); // { "name": "zhang", "gender": "女", "height": "158", "title": "程序员成长指北" } }};</script><style scoped>div{ background: #0f0; margin: 20px}</style>

June 15, 2019 · 2 min · jiezi

Vuejs-构建你的第一个包并在NPM上发布

本文我们将学习如何制作一个vue插件,并将其分发到npm上,能够让其他人安装使用.插件大大地提高了开发者的开发效率。我们的大多数项目都依赖于它们,因为它们能够以极快的速度发布新功能。 正如官方Vue.js文档中所述,插件的范围没有限制。通常我们想实现的功能有下面5种: 添加全局方法或者属性 (如: vue-custom-element)添加全局资源:指令/过滤器/过渡等 (如:vue-touch)通过全局 mixin 方法添加一些组件选项 (如:vue-router)添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现 (如:vue-axios)一个库,提供自己的 API,同时提供上面提到的一个或多个功能(如:vue-router)OK,现在你了解了vue插件是什么了,以及它可以满足哪些需求! 如何在vue项目中使用插件通过npm install或yarn add安装插件后,你需要在main.js文件中导入它并调用Vue.use()全局方法。 注意:在new Vue() 前,必须先实例化所有插件.import Vue from "vue";import MyPlugin from "myplugin";Vue.use(MyPlugin);new Vue({// [...]})如果插件包支持cdn方式引用的话,也可以通过以下方式引用: <script src="https://cdn.xxx.cn/npm/myplugin@latest/dist/myplugin.min.js"></script>另外,在你调用Vue.use()时,想对插件做一些自定义配置,你可以这么做: Vue.use(MyPlugin, { option1: false, option2: true})举个例子,比如在引入热门的Element UI库时,它支持传入一个全局配置对象 import Element from 'element-ui';import 'element-ui/lib/theme-chalk/index.css';Vue.use(Element, { // size 用于改变组件的默认尺寸,zIndex 设置弹框的初始 z-index(默认值:2000) size: 'small', zIndex: 3000});现在让我们进入正题!开始构建你的第一个vue插件???? 来制作一个酷炫的按钮组件作为一个有追求的前端,相信你们在公司开发项目时,肯定会想过,"要是公司有属于自己的一套UI组件库,那肯定很棒!"。如果你有这个想法,那你认真看完这篇文章后,将会给你带来很多灵感和启发。 步骤 1:初始化插件目录结构先创建一个空的项目文件夹,名字随意取,然后初始化生成package.json文件(文件的内容后面会介绍) $ mkdir ku-button && cd ku-button$ npm init# 上面这个命令会提示一些问题,一直回车就行,然后最后会创建一个package.json文件然后在项目根目录中创建一个src文件夹,里面新建一个KuButton.vue组件,这里你甚至可以通过vue的vue serve和vue build命令行来对单个*.vue文件进行快速原型开发,不过前提需要先额外安装一个全局的扩展 ...

April 30, 2019 · 3 min · jiezi

Angular-8-组件

创建一个组件独立模板ng g c user-list内联模板ng g c user-list -it显示值插值法 {{}}绑定值从数据流向划分为 3 种: 数据源 -> Template{{expression}}[target]="expression"bind-target="expression"Template -> 数据源(target)="statement"on-target="statement"Template <-> 数据源[(target)]="expression"bindon-target="expression"绑定理解的误区 <button [disabled]="isUnchanged">Save</button> 这并不是将 isUnchanged 的值绑定到了 button 的 disabled attribute 上,而是设置在 该 DOM 元素的 property disabled 上。 HTML attribute 与 DOM property 的对比 HTML attribute 用来初始化 DOM propertyattribute 一旦设置则不可修改,property 可以修改attribute 可以理解为初始值,property 理解为当前值attribute 与 property 并不是完全对应的模板绑定是通过 property 和 event 来工作的,而不是 attribte 绑定目标绑定的目标可以是 property、event、property+event(双向) 绑定目标汇总表属性绑定(property)[property] 形式<img [src]="url">bind-property 形式<img bind-src="url">不应该绑定一个会修改其他任何值的表达式(属性、函数),这可能会导致意料之外的问题 [hero]="currentHero" 与 hero="currentHero" 是不同的 ...

April 29, 2019 · 2 min · jiezi

一个代码仓库,理解 Vue 项目中组件之间的通信

父组件传给子组件:1、props 方式:父组件通过 Prop 向子组件传递数据(推荐使用)项目中访问路由:http://localhost:8080/parent_to_child/props2、slot 方式:父组件通过 v-slot 设置值,子组件通过带 name 的 slot 接收值(推荐使用)项目中访问路由:http://localhost:8080/parent_to_child/slot3、refs 方式:父组件通过 ref 设置子组件的值(可以使用)项目中访问路由:http://localhost:8080/parent_to_child/refs4、$children 方式:父组件通过 $children 设置子组件的值($children 并不保证顺序,开发者不应该依赖子组件的顺序,也不是响应式的)(节制使用)项目中访问路由:http://localhost:8080/parent_to_child/children子组件传给父组件:1、自定义事件方式:父组件通过 v-on 监听子组件的事件,然后父组件使用子组件触发事件抛出的值(本例中,值是一个1到100之内的随机数)项目中访问路由:http://localhost:8080/child_to_parent/v-on_emit2、子组件直接修改父组件传来的 prop子组件可以直接修改父组件传来的引用类型 prop;子组件直接修改接收的基本类型 prop,会报错(如果一定要这样做,以 update:myPropName 的模式触发事件实现,即.sync 修饰符方式)项目中访问路由:http://localhost:8080/child_to_parent/child_component_change_prop3、.sync 修饰符 方式:子组件修改父组件传来的 prop,然后 emit 一个 update 事件,父组件监听该事件并更新自己的 prop(是自定义事件的语法糖)项目中访问路由:http://localhost:8080/child_to_parent/sync_emit_update4、$parent 方式:子组件通过 $parent 设置父组件的值(节制使用)项目中访问路由: http://localhost:8080/child_to_parent/parent

April 11, 2019 · 1 min · jiezi

自定义元素探秘及构建可复用组件最佳实践

原文请查阅这里,略有删减,本文采用知识共享署名 4.0 国际许可协议共享,BY Troland。这是 JavaScript 工作原理第十九章。概述在 前述文章中,我们介绍了 Shadow DOM 接口和一些其它概念,而这些都是网页组件的组成部分。网页组件背后的思想即通过创建颗粒化,模块化和可复用的元素来扩展 HTML 内置功能。这是一个已经被所有主流浏览器兼容的相对崭新的 W3C 标准且可以被用在生产环境之中,虽然不兼容的浏览器需要使用垫片库(将在随后的章节中进行讨论)。正如开发者所知,浏览器为构建网站和网页程序提供了一些重要的开发工具。我们所说的 HTML,CSS 和 JavaScript 即开发者使用 HTML 来构建结构,CSS 进行样式化然后使用 JavaScript 来让页面动起来。然而,在网页组件出现之前,把 JavaScript 脚本和 HTML 结构组合起来并非易事。本文将阐述网页组件的基石-自定义元素。总之,开发者可以使用自定义元素接口来创建包含 JavaScript 逻辑和样式的自定义元素(正如名称的字面意思)。许多开发者会把自定义元素和 shadow DOM 混为一谈。但是,他们是完全不同的概念且它们互补而不是可以相互替代的。一些框架(比如 Angular,React) 试图通过引进其自有概念来解决同样的问题。开发者可以把自定义元素和 Angular 的指令或者 React 组件进行对比。然而,自定义元素是浏览器原生的且只需要原生 JavaScript,HTML 和 CSS。当然了,这并不意味着它可以取代一个典型的 JavaScript 框架。现代框架不仅仅为开发者提供模仿自定义元素行为的能力。因此,可以同时使用框架和自定义元素。接口在深入了解之前,让我们先大概快速浏览一下接口的内容。全局 customElements 对象为开发者提供了一些方法:define(tagName, constructor, options) -创建一个新的自定义元素。包含三个参数:自定义元素的可用标签名称,自定义元素类定义及选项参数对象。目前仅支持一个选项参数:extends 指定想要扩展的 HTML 内置元素名称的字符串。用来创建定制化内置元素。get(tagName) -若元素已经定义则返回自定义元素的构造函数否则返回 undefined。只有一个参数:自定义元素的可用标签名称。whenDefined(tagName)-返回一个 promise 对象,当定义自定义元素即解析。若元素已定义则立即进行解析。若自定义元素标签名称不可用则摒弃 promise。只有一个参数:自定义元素的可用标签名称。如何创建自定义元素创建自定义元素实际上就是小菜一碟。开发者只需要做两件事:创建扩展 HTMLElement 类元素的类定义,然后以合适的名称注册元素。class MyCustomElement extends HTMLElement { constructor() { super(); // … } // …}customElements.define(‘my-custom-element’, MyCustomElement);或者如你所愿,可以使用匿名类以防止弄乱当前作用域customElements.define(‘my-custom-element’, class extends HTMLElement { constructor() { super(); // … } // …});从以上例子可见,使用 customElements.define(…) 方法注册自定义元素。自定义元素所解决的问题实际上,问题是啥?嵌套 DIV 是问题之一。嵌套 Div 是啥?在现代网页程序中这是一个非常常见的现象,开发者会使用多个嵌套块状元素(div 互相嵌套之类)。<div class=“top-container”> <div class=“middle-container”> <div class=“inside-container”> <div class=“inside-inside-container”> <div class=“are-we-really-doing-this”> <div class=“mariana-trench”> … </div> </div> </div> </div> </div></div>因为浏览器可以在页面上正常进行渲染,所以使用了这样的嵌套结构。但是,这会使得 HTML 不具可读性且难以维护。因此,例如假设有如下组件:那么传统 HTML 结构类似如下:<div class=“primary-toolbar toolbar”> <div class=“toolbar”> <div class=“toolbar-button”> <div class=“toolbar-button-outer-box”> <div class=“toolbar-button-inner-box”> <div class=“icon”> <div class=“icon-undo”>&nbsp;</div> </div> </div> </div> </div> <div class=“toolbar-button”> <div class=“toolbar-button-outer-box”> <div class=“toolbar-button-inner-box”> <div class=“icon”> <div class=“icon-redo”>&nbsp;</div> </div> </div> </div> </div> <div class=“toolbar-button”> <div class=“toolbar-button-outer-box”> <div class=“toolbar-button-inner-box”> <div class=“icon”> <div class=“icon-print”>&nbsp;</div> </div> </div> </div> </div> <div class=“toolbar-toggle-button toolbar-button”> <div class=“toolbar-button-outer-box”> <div class=“toolbar-button-inner-box”> <div class=“icon”> <div class=“icon-paint-format”>&nbsp;</div> </div> </div> </div> </div> </div></div>但想象下如果可以使用类似如下代码:<primary-toolbar> <toolbar-group> <toolbar-button class=“icon-undo”></toolbar-button> <toolbar-button class=“icon-redo”></toolbar-button> <toolbar-button class=“icon-print”></toolbar-button> <toolbar-toggle-button class=“icon-paint-format”></toolbar-toggle-button> </toolbar-group></primary-toolbar>要我说,第二个示例清爽多了。第二个示例更具可维护性,可读性且对于浏览器和开发者更加合理。更加简洁。另一个问题即可复用性。作为开发者,不仅仅要书写可运行的代码还得写出可维护代码。书写可维护代码即能够轻易地复用代码片段而不是重复地复制粘贴。我将会给出一个简单的示例而你就会明白。假设有如下元素:<div class=“my-custom-element”> <input type=“text” class=“email” /> <button class=“submit”></button></div>若需要在其它地方使用这段代码,开发者需要再次书写相同的 HTML 结构。现在,想象 一下需要稍微修改一下这些元素。开发者需要找出每个代码需要修改的地方,然后一遍遍地做出同样的修改。太恶心了。。。若使用如下码岂不会更好?<my-custom-element></my-custom-element>现代网页程序不仅仅只有静态 HTML。开发者需要做交互。这就需要 JavaScript。一般来说,开发者需要做的即创建一些元素然后在上面监听事件以响应用户输入。点击,拖拽或者悬浮事件等等。var myDiv = document.querySelector(’.my-custom-element’);myDiv.addEventListener(‘click’, () => { myDiv.innerHTML = ‘<b> I have been clicked </b>’;});<div class=“my-custom-element”> I have not been clicked yet.</div>使用自定义元素接口可以把所有的逻辑封装进元素自身。以下代码可以实现和上面代码一样的功能:class MyCustomElement extends HTMLElement { constructor() { super(); var self = this; self.addEventListener(‘click’, () => { self.innerHTML = ‘<b> I have been clicked </b>’; }); }}customElements.define(‘my-custom-element’, MyCustomElement);<my-custom-element> I have not been clicked yet</my-custom-element>咋一看上去,自定义元素技术需要书写更多的 JavaScript 代码。但是在实际程序中,创建不需复用的单一组件的情况是很少见的。一个典型的现代网页程序的重要特征即大多数元素都是动态创建的。那么,开发者就需要分别处理使用 JavaScript 动态添加元素或者使用 HTML 结构中预定义内容。那么可以使用自定义元素来实现这些功能。总之,自定义元素让开发者的代码更易理解和维护,并分割为小型,可复用及可封装的模块。要求在创建自定义元素之前,开发者需要遵守如下特殊规则:名称必须包含一个破折号 - 。这样 HTML 解析器就可以把自定义元素和内置元素区分开来。这样可以保证不会和内置元素出现命名冲突的问题(不管是现在或者将来当添加其它元素的时候)。比如,<my-custom-element> 是正确的而 myCustomElement 和 <my_custom_element> 则不然。不允许重复注册标签名称。重复注册标签名称会导致浏览器抛出 DOMException 错误。不可以覆盖已注册自定义元素。自定义元素不可以自关闭。HTML 解析器只允许一小撮内置元素可以自关闭(比如 <img>,<link>,<br>)。功能那么究竟自定义元素可以实现哪些功能?答案是很多。最好用的功能之一即元素的类定义可以引用 DOM 元素自身。这意味着开发者可以直接使用 this 来直接监听事件,访问 DOM 属性,访问 DOM 元素子节点等等。class MyCustomElement extends HTMLElement { // … constructor() { super(); this.addEventListener(‘mouseover’, () => { console.log(‘I have been hovered’); }); } // …}当然,这样开发者就可以使用新内容来覆盖元素的子节点。但一般不推荐这样做,因为这可能会导致意外的行为。作为自定义元素的使用者,因为不是使用者开发的,当元素里面的标记被其它内容所取代,用户会觉得很奇怪。在元素生命周期的特定阶段,开发者可以在一些生命周期钩子中执行代码。constructor每当创建或者更新元素会触发构造函数(随后再详细讲解下)。一般情况会在该阶段初始化状态,监听事件,创建 shadow DOM 等等。需要记住的是必须总是在构造函数中调用 super()。connectedCallback每当在 DOM 中添加元素的时候会调用 connectedCallback 方法。可以用来(推荐)延迟执行某些代码直到元素完全渲染于页面上时候调用(比如获取一个资源)。disconnectedCallback与 connectedCallback 相反,当元素被从 DOM 删除时调用 disconnectedCallback 方法。一般用于释放资源的时候调用。需要注意的是若用户关闭选项卡不会调用 disconnectedCallback 方法。因此,首先开发者需要注意初始化代码。attributeChangedCallback每当添加,删除,更新或者替换元素的某一属性的时候调用。当解析器创建的时候也会调用。但是,请注意只有在 observedAttributes 属性白名单中的属性才会触发。addoptedCallback当使用 document.adoptNode(…) 来把元素移动到另一个文档的时候会触发 addoptedCallback方法。请注意以上所有的回调都是同步。例如,当把元素添加进 DOM 的时候只会触发连接回调。属性反射内置 HTML 元素提供了一个非常方便的功能:属性反射。这意味着直接修改某些属性值会直接反射到 DOM 的属性中。例如 id 属性:myDiv.id = ’new-id’;将会更新 DOM 为<div id=“new-id”> … </div>反之亦然。这是非常有用的因为这样就使得开发者可以声明式书写元素。自定义元素自身没有该功能,但是有办法可以实现。为了在自定义元素中实现该相同的功能,开发者需要定义属性的 getters 和 setters 方法。class MyCustomElement extends HTMLElement { // … get myProperty() { return this.hasAttribute(‘my-property’); } set myProperty(newValue) { if (newValue) { this.setAttribute(‘my-property’, newValue); } else { this.removeAttribute(‘my-property’); } } // …}扩展元素开发者不仅仅可以使用自定义元素接口创建新的 HTML 元素还可以用来扩展现有的 HTML 元素。而且该接口在内置元素和其它自定义元素中工作得很好。仅仅只需要扩展元素的类定义即可。class MyAwesomeButton extends MyButton { // …}customElements.define(‘my-awesome-button’, MyAwesomeButton);或者当扩展内置元素时,开发者需要为 customElements.define(…) 函数添加第三个 extends 的参数,参数值为需要扩展的元素标签名称。由于许多内置元素共享相同的 DOM 接口,extends 参数会告诉浏览器需要扩展的目标元素。若没有指定需要扩展的元素,浏览器将不会知道需要扩展的功能类别 。class MyButton extends HTMLButtonElement { // …}customElements.define(‘my-button’, MyButton, {extends: ‘button’});一个可扩展原生元素也被称为可定制化内置元素。开发者需要记住的经验法则即总是扩展存在的 HTML 元素。然后,一点点往里添加功能。这样就可以保留元素之前的功能(属性,函数)。请注意现在只有 Chrome 67+ 才支持定制化内置元素。以后,其它浏览器也会实现,但是 Safari 完全没有实现该功能。更新元素如上所述,可以使用 customElements.define(…) 方法注册自定义元素。但这并不意味着,开发者必须首先注册元素。可以推迟在之后某个时间注册自定义元素。甚至可以在往 DOM 中添加元素后再注册元素也是可以的。这一过程称为更新元素。开发者可以使用 customElements.whenDefined(…) 方法获取元素的定义时间。开发者传入元素标签名,返回一个 promise 对象,然后当元素注册的时候解析。customElements.whenDefined(‘my-custom-element’).then(_ => { console.log(‘My custom element is defined’);});例如,开发者也许想要延迟执行代码直到定义元素内所有子元素。若内嵌自定义元素,这将会非常有用。有时候,父元素有可能会依赖于其子元素的实现。在这种情况下,开发者需要确保子元素在其父元素之前定义。Shadow DOM如前所述,需要把自定义元素和 shadow DOM 一起使用。前者用来把 JavaScript 逻辑封装进元素而后者用来为一小段 DOM 创建一个不为外部影响的隔绝环境。建议查看之前专门介绍 shadow DOM 的文章以便更好地理解 shadow DOM 概念。只需调用 this.attachShadow 就可以在自定义元素内使用 shadow DOMclass MyCustomElement extends HTMLElement { // … constructor() { super(); let shadowRoot = this.attachShadow({mode: ‘open’}); let elementContent = document.createElement(‘div’); shadowRoot.appendChild(elementContent); } // …});模板我们在之前的文章中简单介绍了下模板,需要单独一篇文章来专门介绍模板。这里,我们将会给出一个简单的示例来介绍如何在自定义元素中使用模板。通过声明一个 DOM 片段来使用 <template>,该标签内容只会被解析而不会在页面上渲染。<template id=“my-custom-element-template”> <div class=“my-custom-element”> <input type=“text” class=“email” /> <button class=“submit”></button> </div></template>let myCustomElementTemplate = document.querySelector(’#my-custom-element-template’);class MyCustomElement extends HTMLElement { // … constructor() { super(); let shadowRoot = this.attachShadow({mode: ‘open’}); shadowRoot.appendChild(myCustomElementTemplate.content.cloneNode(true)); } // …});那么现在,我们在自定义元素里面使用了 shadow DOM 和 模板,创建了一个元素,该元素作用域和其它元素隔绝且把 HTML 结构和 JavaScript 逻辑完美地隔离开来。样式化那么,我们讲解了 HTML 和 JavaScript,现在还剩下 CSS。显然,需要样式化元素。开发者可以在 shadow DOM 中添加样式但是用户如何从外部样式化元素呢?答案很简单-只需要和一般的内置元素一样写样式即可。my-custom-element { border-radius: 5px; width: 30%; height: 50%; // …}请注意外部定义的样式比元素内部定义的样式优先级高,外部样式会覆盖掉元素内定义的样式。开发者需要明白有时候页面渲染,然后会在某些时刻会发现无样式内容闪烁(FOUC)。开发者可以通过为未定义组件定义样式及当元素已定义的时候使用一些动画过渡效果。使用 :defined 选择器来达成这一效果。my-button:not(:defined) { height: 20px; width: 50px; opacity: 0;}未知元素对比未定义自定义元素HTML 规范非常灵活且允许开发者任意声明标签。若不被浏览器解析则会解析为 HTMLUnknownElement。var element = document.createElement(’thisElementIsUnknown’);if (element instanceof HTMLUnknownElement) { console.log(‘The selected element is unknown’);}但是这并不适用于自定义元素。还记得讨论定义自定义元素时候的特殊命名规则吗?原因是因为当浏览器发现一个自定义元素的名称有效的时候,浏览器会把它解析为 HTMLElement ,然后浏览器会把它看作一个未定义的自定义元素。var element = document.createElement(’this-element-is-undefined’);if (element instanceof HTMLElement) { console.log(‘The selected element is undefined but not unknown’);}在视觉上, HTMLElement 和 HTMLUnknownElement 可能没啥不同,但是需要注意其它地方。解析器会区别对待这两种元素。具有有效自定义名称的元素会被看作拥有自定义元素实现。在定义实现细节之前该自定义元素会被看成一个空 div 元素。而一个未定义元素没有实现任何内置元素的任何方法或属性。浏览器兼容custom elements 第一版是在 Chrome 36+ 中引入的。被称为自定义元素接口 v0,虽然现在仍然可用,但是已经被弃用并被认为是糟糕的实现。若想要学习 v0 版,可以阅读这篇文章。从 Chrome 54 和 Safari 10.1(虽然只有部分支持) 开始支持自定义元素接口 v1,微软 Edge 还处于其原型设计阶段而 Mozilla 从 v50 开始支持,但默认不支持需要显式启用。目前只有 webkit 浏览器完全支持。然而,如上所述,可以使用垫片库兼容到包括 IE11 在内的所有浏览器。检测可用性通过检查 window 对象中的 customElements 属性是否可用来检查浏览器是否支持自定义元素。const supportsCustomElements = ‘customElements’ in window;if (supportsCustomElements) { // 可以使用自定义元素接口}否则需要使用垫片库:function loadScript(src) { return new Promise(function(resolve, reject) { const script = document.createElement(‘script’); script.src = src; script.onload = resolve; script.onerror = reject; document.head.appendChild(script); });}// Lazy load the polyfill if necessary.if (supportsCustomElements) { // 浏览器原生支持自定义元素} else { loadScript(‘path/to/custom-elements.min.js’).then(_ => { // 加载自定义元素垫片 });}总之,网页组件标准中的自定义元素为开发者提供了如下功能:把 JavaScript 和 CSS 样式整合入 HTML 元素允许开发者扩展已有的 HTML 元素(内置和其它自定义元素)不需要其它库或者框架的支持。只需要原生 JavaScript,HTML 和 CSS 还有可选的垫片库来支持旧浏览器。可以和其它网页组件功能无缝衔接(shadow DOM,模板,插槽等)。和浏览器开发者工具紧密集成在一起。使用已知的可访问功能总之,自定义元素和开发者已经使用过的组件技术并没有什么大的不同。它只让开发网页程序过程更加便携的另一种方式。那么,它让更快地构建非常复杂的程序成为可能。参考资料:https://developers.google.com…https://www.html5rocks.com/en...https://github.com/w3c/webcom… ...

March 1, 2019 · 4 min · jiezi

Vue自定义组件(简单实现一个自定义组件)

在用vue构建项目的过程中,我们有时会用到别人开发的组件如vue-router;使用他人组件的正常步骤如下:1、命令行进行安装,执行install;2、在vue项目中的入口文件main.js中,进行导入;3、然后用Vue.use(plugin)引入该组件。我们也可以创造属于自己的组件,具体步骤如下:1、在components文件中创建test文件;2、在test文件中,创建index.js和Test.vue;3、Test.vue中的代码如下:<template> <div> <div>实现自定义组件</div> </div></template><script>export default{ data () { return { msg: ‘hello vue’ } }, components: {}}</script><style></style>4、test文件夹下的index.js中的代码如下:import MyTest from ‘./Test.vue’const Test = { install (Vue) { Vue.component(‘Test’, MyTest) }}export default Test5、入口文件main.js进行相关的配置:import Test from ‘./components/test’Vue.use(Test)6、如此这般,就可以在其它组件中正常使用,如下:<template> <div class=“hello”> <Test></Test> </div></template>自定义组件Test的内容(“实现自定义组件”)将会展示出来。注:test文件指的是自定义组件文件夹;index.js指的是组件的入口加载文件;Test.vue指的是组件模板。

January 26, 2019 · 1 min · jiezi

vue组件封装及父子组件传值,事件处理

vue开发中,把有统一功能的部分提取出来,作为一个独立的组件,在需要使用的时候引入,可以有效减少代码冗余.难点在于如果封装,使用,如何传参,派发事件等,我会采取倒叙的方式进行说明.(本文总结于Vue2实战解密一书)代码如下:封装组件BookList.vue<template> <div class=“book-list”> <div class=“header”> <div class=“heading”>{{heading}}</div> <div class=“more”>更多…</div> </div> <div class=“book-items”> <div class=“book” v-for=“book in books”> <div class=“cover”> <img :src=“book.imgUrl” /> </div> <div>{{book.title}}</div> <div>{{book.authors | join}}</div> </div> </div> </div></template><script> export default{ props:[‘heading’,‘books’], filters:{ join(args){ return args.join(’,’); } } }</script>要向组件输入数据就不能用data来作为数据容器了,因为data是一个内部对象,此时要换成props我们可以这样理解: data的作用域是仅仅适用于内部,而对于外部是不可见的,props是内部外部都可见,是一个公共的组件成员变量.Home.vue 组件代码如下:<template> <div class=“section”> <book-list :books=“books1” heading=“书列表1”></book-list> </div> <div class=“section”> <book-list :books=“books2” heading=“书列表2”></book-list> </div></template><script> import BookList from ‘./components/BookList.vue’ export default{ data(){ return{ books1:[], books2:[], } }, components:{ //注册自定义组件 BookList }</script> ...

January 11, 2019 · 1 min · jiezi

组件调用错误,路径问题

使用vue-cli构建项目,在项目进行的过程中,某个详情页调用组件报错,在别的页面中同样调用过该组件,但是没有报错。错误信息如下:This dependency was not found:* play.vue in ./node_modules/babel-loader/lib!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./src/components/detail/detail.vueTo install it, you can run: npm install –save play.vue引用组件代码:<script> import play from ‘play.vue’; import foot_contact from ‘../home/foot_contact.vue’; export default { name: “detail”, components: { play, foot_contact, }, data() { return { activeIndex: ‘1’, activeIndex2: ‘1’ } }, methods: { handleSelect(key, keyPath) { console.log(key, keyPath); } } }</script>经查证,是路径问题。因为子组件与父组件在同一个目录下,所以引入子组件时,我直接使用了组件的文件名(play.vue)。这使得vue在编译时,把它当做node_modules下的模块,因此要求你安装。在项目中引用组件是需要写相对路径的,即使是在同一目录下。在我的项目中,需要改正的也就是’./play.vue’。在信息时代,知识的分享使得问题的解决比以前容易很多,只有不断总结与前进,你才能与时俱进。感谢网友们的分享:https://segmentfault.com/q/10…

January 3, 2019 · 1 min · jiezi

微信小程序之店铺评分组件及vue中用svg实现的评分显示组件

在微信小程序中,有遇到要展示店铺评分,或者是订单完成后对商品进行评价,用到了星星展示,查了下,在微信中无法使用svg实现图片,微信中只能将svg图片转成base64来显示,所以是在vue中使用的svg来实现评分1.效果图微信中的可以点击及显示,但是,显示的话,在4.2分,4点多分的时候,显示的是半颗星vue中用的是svg实现,所以用的是占比的形式,可以有一点点的星星星图片2.微信实现店铺评分显示及商品评价星星展示子组件index.wxml,可以动态的控制星星的大小<!– (size * stars.length + (size/2) * 4 + 20 )这里的话,是在可以点击的时候,加上了好评的字体的长度 –><view class=‘starsBox’ style=‘width:{{isClick?(size * stars.length + (size/2) * 4 + 20 ):(size * stars.length)}}rpx;height:{{size}}rpx;’> <view class=‘stars’ style=‘width:{{size * stars.length}}rpx;height:{{size}}rpx;’> <block wx:for="{{stars}}" wx:key="{{index}}"> <image src="/images/{{item == 0 ? ‘grayStar’:item}}.png" style=‘width:{{size}}rpx;height:{{size}}rpx;’ data-index="{{index}}" catchtap=“computeScore”></image> </block> </view> <view wx:if="{{isClick}}" class=‘text’ style=‘font-size:{{size/2}}rpx;’> <text wx:if="{{value==‘0’}}" class=‘pointText’>暂无评分</text> <text wx:elif="{{value==‘1’}}" class=‘pointText’>差评</text> <text wx:elif="{{value<‘4’}}" class=‘pointText’>中评</text> <text wx:else class=‘pointText’>好评</text> </view></view>子组件index.wxss.starsBox{ display: flex; align-items: center; justify-content: flex-start;}.stars{ width: 150rpx; height: 50rpx; display: flex; align-items: center; justify-content: flex-start;}.stars image{ width: 30rpx; height: 30rpx;}.text{ color: #ccc; margin-left: 20rpx;}子组件index.jsComponent({ properties: { /* 显示有色星星的个数 / value: { type: Number, value: 0, / 监听value值的变化 / observer: function (newVal, oldVal, changedPath) { this.init() } }, / 设置星星大小 / size: { type: Number, value: 30 }, / 是否可点击,type为null表示值可以是任意类型 / isClick: { type: null, value: false } }, attached() { / 组件生命周期函数,在组件实例进入页面节点树时执行 / this.init(); }, data: { stars: [0, 0, 0, 0, 0] }, methods: { init() { let star = this.properties.value; let stars = [0, 0, 0, 0, 0]; / 图片名称,通过设置图片名称来动态的改变图片显示 / for (let i = 0; i < Math.floor(star); i++) { stars[i] = ‘star’; } if (star > Math.floor(star)) { stars[Math.floor(star)] = ‘halfStar’; } for (let i = 0; i < stars.length; i++) { if (stars[i] == 0) { stars[i] = ‘grayStar’; } } this.setData({ stars }) }, / 可点击时,用于计算分数 / computeScore(e) { let index = e.currentTarget.dataset.index; let isClick = this.data.isClick; if (isClick) { let score = index + 1; this.triggerEvent(‘compute’, { score }); } } }})3.父组件中引用父组件index.wxml<view class=“score”> <view class=“scoreItem”> <score value="{{shopGrade}}" size=“46” isClick=“true” bindcompute=“computeGrade” /> </view> <view class=“scoreItem”> <score value="{{shopGrade1}}" size=“46” /> </view></view>父组件index.json{ “usingComponents”: { “score”: “/component/score/index” }}父组件index.jsdata: { shopGrade: 0, shopGrade1: 4.6,},/ 评分处理事件 /computeGrade(e) { let score = e.detail.score; this.setData({ shopGrade: score })},4.vue中使用svg实现评分首先在vue使用的index.html的模板文件中添加一个rem转换算法,因为我后面用的单位是rem/ 在头部添加 /<script type=“text/javascript”> document.getElementsByTagName(“html”)[0].style.fontSize = 100 / 750 * window.screen.width + “px”; </script>然后添加svg.vue文件,这个svg文件可以自己找图片生成,并设置对应的id<template> <svg xmlns=“http://www.w3.org/2000/svg" xmlns:xlink=“http://www.w3.org/1999/xlink" style=“position:absolute;width:0;height:0;visibility:hidden”> <defs> <symbol id=“star” viewBox=“0 0 32 32”> <path class=“path1” d=“M16 26.382l-8.16 4.992c-1.5 0.918-2.382 0.264-1.975-1.435l2.226-9.303-7.269-6.218c-1.337-1.143-0.987-2.184 0.755-2.322l9.536-0.758 3.667-8.835c0.674-1.624 1.772-1.613 2.442 0l3.667 8.835 9.536 0.758c1.753 0.139 2.082 1.187 0.755 2.322l-7.269 6.218 2.226 9.303c0.409 1.71-0.485 2.347-1.975 1.435l-8.16-4.992z”> </path> </symbol> </defs> </svg></template><script></script><style></style>rating.vue文件引用svg.vue<template> <div class=“ratingstar”> <section class=“star_container”> <svg class=“grey_fill” v-for="(num,index) in 5” :key=“index”> <use xmlns:xlink=“http://www.w3.org/1999/xlink" xlink:href="#star”></use> </svg> </section> <div class=“star_overflow” :style="‘width:’+rating2/10+‘rem’"> <section class=“star_container”> <svg class=“orange_fill” v-for="(num,index) in 5” :key=“index”> <use xmlns:xlink=“http://www.w3.org/1999/xlink" xlink:href="#star”></use> </svg> </section> </div> <svgIcon></svgIcon> </div></template><script> import svgIcon from ‘@/components/svg’ export default { components: { svgIcon }, data() { return { rating: 4.2 } }, }</script><style lang=“less” rel=“stylesheet/less” scoped> .ratingstar { position: relative; width: 100%; .star_overflow { overflow: hidden; position: relative; height: 0.65rem; } .star_container { position: absolute; top: 0.05rem; width: 1rem; display: flex; justify-content: flex-start; align-items: center; .grey_fill { width: 0.94rem; height: 0.2rem; fill: #d1d1d1; } .orange_fill { width: 0.94rem; height: 0.2rem; fill: #ff9a0d; } } }</style>都有用到组件,可以查看下方的推荐文章中的购物车中的父子组件传值正在努力学习中,若对你的学习有帮助,留下你的印记呗(点个赞咯^_^)往期好文推荐:判断iOS和Android及PC端实现单行文字及多行文字的省略号微信小程序之购物车和父子组件传值及calc的注意事项纯css实现瀑布流(multi-column多列及flex布局) ...

November 15, 2018 · 3 min · jiezi

微信小程序之购物车和父子组件传值及calc的注意事项

在做微信小程序时,觉得小组里对购物车的实现不是很完美,就自己尝试的写了下,然后用到了父子组件传值,父子组件传值的话,和vue框架上是非常相似的,以及calc这个css函数,calc有个注意点,自己不怎么用,一时间有差点忘了,这里记录下1.效果图2.子组件实现要实现图中删除的效果,使用组件的形式更好做点,我当时本想直接在pages里实现,不过结果就是,滑动时,所有的商品都显示了删除按钮,除非用数组将每个商品要移动的距离存储起来,不过这样的话就很麻烦,所以我也是用组件来实现的关于微信组件,可以直接点击链接访问官网查看自定义组件子组件index.wxml<view class=“commodityItem” bindtouchstart=“handleTouchStart” bindtouchmove=“handleTouchMove” style=“transform:translateX({{-rightSpace}}px)"> <view class=“selectedBtn” bindtap=“handleSelect” data-is-selected=”{{commodity.isselected}}"> <view class=“noSelected” wx:if="{{commodity.isselected==0}}"></view> <image class=“selectedImg” wx:else src="/images/selected.png"></image> </view> <view class=“commodityInfo”> <view class=“commodityImg”> <image src="{{commodity.image}}"></image> </view> <view class=“commodityTitle”> <view class=“title”>{{commodity.title}}</view> <view class=“standard”>规格:{{commodity.standard?commodity.standard:‘无’}}</view> <view class=“count”> <view class=“price”>¥{{commodity.price}}</view> <view class=“commodityNum”> <i-input-number value="{{selectedNum}}" min=“1” max="{{commodity.stock}}" bindchange=“numChange” /> </view> </view> </view> </view> <view class=“deleteBtn”> <image class=“deleteImg” src="/images/delete.png"></image> <text class=“deleteText”>删除</text> </view></view>子组件index.wxss/* 商品 /.commodityItem{ display: flex; position: relative; padding: 10rpx 24rpx 20rpx 30rpx; box-sizing: border-box; background: #fff; transition: all .5s;}/ 选择按钮 /.selectedBtn{ display: flex; align-items: center; width: 80rpx;}.noSelected{ width: 46rpx; height: 46rpx; border-radius: 50%; border: 1px solid #ef5225;}.selectedBtn .selectedImg{ width: 50rpx; height: 50rpx;}/ 商品信息 /.commodityInfo{ display: flex; width: calc(100% - 80rpx);}.commodityImg{ margin-right: 18rpx; width: 220rpx; height: 220rpx;}.commodityImg image{ width: 100%; height: 100%; vertical-align: middle; }/ 商品title /.commodityTitle{ width: calc(100% - 220rpx);}.title{ display: -webkit-box; width: 100%; height: 70rpx; line-height:35rpx; font-size: 24rpx; font-weight:600; overflow: hidden; -webkit-line-clamp: 2; -webkit-box-orient: vertical;}.standard{ padding-top: 16rpx; width: 100%; height: 90rpx; box-sizing: border-box;}.count{ display: flex; align-items: center; justify-content: space-between; width: 100%; height: 60rpx;}/ 删除按钮 /.deleteBtn{ display: flex; position: absolute; width: 70px; height: 100%; top: 0rpx; right: -70px; flex-direction: column; align-items: center; justify-content: center; background: #ef5225;}.deleteImg{ margin-bottom: 10rpx; width: 50rpx; height: 50rpx; vertical-align: middle;}.deleteText{ color: #fff;}子组件index.json,这里用了iview中的数字输入框{ “component”: true, “usingComponents”: { “i-input-number”: “/component/iview/input-number/index” }}子组件index.jsComponent({ properties: { commodity: Object, }, data: { touchStart: null, rightSpace: 0, selectedNum: 1, }, methods: { / 商品是否选中 / handleSelect() { let selectedNum = this.data.selectedNum; let commodity = this.data.commodity; if(commodity.isselected == 0) { commodity.isselected = 1; } else { commodity.isselected = 0; } this.triggerEvent(‘handleselect’, { commodity, selectedNum}) }, / 处理触摸滑动开始 / handleTouchStart(e) { / 记录触摸滑动初始位置 / let touchStart = e.changedTouches[0].clientX; this.setData({ touchStart }) }, / 处理触摸滑动 / handleTouchMove(e) { console.log(e) let moveSpace = e.changedTouches[0].clientX; let touchStart = this.data.touchStart; if (touchStart != null) { if (moveSpace - touchStart > 70) { this.setData({ touchStart: null, rightSpace: 0 }) } else if (moveSpace - touchStart < -70) { this.setData({ touchStart: null, rightSpace: 70 }) } } }, numChange(e) { let selectedNum = e.detail.value; let commodity = this.data.commodity; this.setData({ selectedNum }) this.triggerEvent(‘handleselect’, { commodity, selectedNum}) } }})3.父组件实现父组件index.wxml,这里用的是假数据,所以操作上会有一些是联调时不必要的操作<view class=“cart”> <view class=“item” wx:for="{{cartList}}" wx:key="{{items.shopid}}" wx:for-item=“items”> <view class=“storeInfo”> <image class=“avatar” src="{{items.logo}}"></image> <view class=“storeName”>{{items.shopname}}</view> </view> <view class=“discount”>满¥100包邮,满10件包邮</view> <view class=“commodity” wx:for="{{items.commodity}}" wx:key="{{item.id}}"> <cart-item commodity="{{item}}" bind:handleselect=“handleSelect” /> </view> </view> <view class=“count”> <view class=“selectAll” bindtap=“handleSelectAll”> <view class=“noSelected” wx:if="{{!isSelectedAll}}"></view> <image class=“selectedImg” wx:else src="/images/selected.png"></image> <text class=“selectAllText”>全选</text> </view> <view class=“countPrice”> <text>合计:</text> <text>¥{{countPrice}}</text> </view> <view class=“account”> <text>结算</text> <text>({{countSelectedNum}})</text> </view> </view></view>父组件index.wxsspage{ background: #f8f8f8;}.cart{ padding-bottom: 100rpx; font-size: 26rpx;}.item{ border-bottom: 1px solid #eee;}/ 头部店铺信息 /.storeInfo{ display: flex; padding: 18rpx 0rpx 18rpx 30rpx; background: #fff; box-sizing: border-box;}.storeInfo .avatar{ width: 56rpx; height: 56rpx; border-radius: 50%; vertical-align: middle;}.storeInfo .storeName{ margin-left: 16rpx; line-height: 56rpx;}/ 包邮信息 /.discount{ padding-left: 30rpx; height:50rpx; line-height: 50rpx; font-size:20rpx; color: #666; box-sizing: border-box;}/ 底部操作 /.count{ display: flex; position: fixed; padding-left: 30rpx; bottom: 0; left: 0; width: 100%; height: 100rpx; line-height: 100rpx; box-sizing: border-box; color: #232323; background: #eee;}/ 全选 /.selectAll{ display: flex; padding-right: 20rpx; align-items: center; width: 25%; font-size: 30rpx;}.selectAll .noSelected{ width: 46rpx; height: 46rpx; border-radius: 50%; border: 1px solid #ef5225;}.selectAll .selectedImg{ width: 50rpx; height: 50rpx;}.selectAllText{ margin-left: 18rpx;}.countPrice{ position: absolute; top: 0; right: 270rpx; height: 100%; line-height: 100rpx; text-align: center; font-size: 30rpx;}.countPrice text{ margin-right: 15rpx;}.account{ position: absolute; top: 0; right: 0; width: 270rpx; height: 100%; line-height: 100rpx; text-align: center; font-size: 30rpx; background: #ef5225; color: #fff;}父组件index.json,引用子组件{ “usingComponents”: { “cart-item”: “/component/cart/index” }}父组件index.jsPage({ data: { cartList: [ { shopname: ‘猫咪小店’, logo: ‘/images/avatar.jpeg’, shopid: 11, commodity: [ { id: 1, image:’/images/commodity.jpg’, title: ‘雅诗兰黛鲜活焕亮红石榴晚霜50ml 补水保湿 滋润排浊’, standard: ‘111 + 黑色’, price: ‘100’, stock: 10, quantity: 1, isselected: 0, }, { id: 2, image:’/images/avatar7.jpg’, title: ‘雅诗兰黛鲜活焕亮红石榴晚霜50ml 补水保湿 滋润排浊’, price: ‘10’, stock: 5, quantity: 1, isselected: 0, } ] }, { shopname: ‘猫咪小店’, logo: ‘/images/avatar5.jpg’, shopid: 450, commodity: [ { id: 3, image:’/images/commodity.jpg’, title: ‘雅诗兰黛鲜活焕亮红石榴晚霜50ml 补水保湿 滋润排浊’, price: ‘90’, stock: 10, quantity: 1, isselected: 0, }, { id: 4, image:’/images/avatar7.jpg’, title: ‘雅诗兰黛鲜活焕亮红石榴晚霜50ml 补水保湿 滋润排浊’, price: ‘100’, stock: 5, quantity: 1, isselected: 0, }, { id: 5, image:’/images/commodity.jpg’, title: ‘雅诗兰黛鲜活焕亮红石榴晚霜50ml 补水保湿 滋润排浊’, standard: ‘111 + 黑色’, price: ‘100’, stock: 2, quantity: 1, isselected: 0, } ] }, { shopname: ‘猫咪小店’, logo: ‘/images/avatar.jpeg’, shopid: 550, commodity: [ { id: 6, image:’/images/avatar8.jpg’, title: ‘雅诗兰黛鲜活焕亮红石榴晚霜50ml 补水保湿 滋润排浊’, standard: ‘111 + 黑色’, price: ‘100’, stock: 1, quantity: 1, isselected: 0, } ] }, ], / 商品是否全选中 / isSelectedAll: false, / 已选中商品的价格 / countPrice: 0, / 统计所有选中的商品数量 / countSelectedNum: 0, }, / 处理商品选中 / handleSelect(e) { let countPrice = 0; let countSelectedNum = 0; let cartList = this.data.cartList; let length = cartList.length; / 因为是假数据,所以需要循环查找到对应的数据将其替换 / for(let i = 0; i < length; i++) { for(let j = 0; j < cartList[i].commodity.length; j++) { if (cartList[i].commodity[j].id == e.detail.commodity.id) { cartList[i].commodity[j] = e.detail.commodity; cartList[i].commodity[j].selectedNum = e.detail.selectedNum; } if (cartList[i].commodity[j].isselected == 1) { / 点击选中的时候,计算价格,要判断下设置的商品选中数量, * 我这里的是对点击了的商品才设置了选中的数量,所以需要对没有点击的商品数量设置为1,然后就默认的加一 / if (cartList[i].commodity[j].selectedNum != undefined) { countPrice += cartList[i].commodity[j].price * cartList[i].commodity[j].selectedNum; countSelectedNum += cartList[i].commodity[j].selectedNum } else { countPrice += cartList[i].commodity[j].price * 1; countSelectedNum += 1; } } } } / 对是否全选中进行判断 / let isSelectedAll = true; for (let i = 0; i < length; i++) { for (let j = 0; j < cartList[i].commodity.length; j++) { / 若商品中的isselecetd有为0的就终止循环,直接设置为未全选 / if (cartList[i].commodity[j].isselected == 0) { isSelectedAll = false; break; } } } this.setData({ cartList, isSelectedAll, countPrice, countSelectedNum }) }, / 全选中商品 / handleSelectAll() { let isSelectedAll = !this.data.isSelectedAll; let cartList = this.data.cartList; let length = cartList.length; let countPrice = 0; let countSelectedNum = 0; / 遍历数据中的isselected来进行全选的操作 / for(let i = 0; i < length; i++) { for (let j = 0; j < cartList[i].commodity.length; j++) { if(isSelectedAll) { cartList[i].commodity[j].isselected = 1; / 全选的时候,计算价格,要判断下设置的商品选中数量, * 我这里的是对点击了的商品才设置了选中的数量,所以需要对没有点击的商品数量设置为1,然后就默认加一 / if (cartList[i].commodity[j].selectedNum != undefined) { countPrice += parseInt(cartList[i].commodity[j].price) * cartList[i].commodity[j].selectedNum; countSelectedNum += cartList[i].commodity[j].selectedNum; } else { countPrice += cartList[i].commodity[j].price * 1; countSelectedNum += 1; } } else { cartList[i].commodity[j].isselected = 0; } } } this.setData({ isSelectedAll, cartList, countPrice, countSelectedNum }) },})4.父子组件传值较常用的都是父组件往子组件传值,所以子组件往父组件传值就会不是很熟悉我这里的话,是因为用的假数据,在点击商品选中或者不选中时,需要改变商品里的选中属性,所以用到了子组件往父组件传值,也包括传递选中的商品数量子组件往父组件传值的话,是通过在调用this.triggerEvent()来实现的/ 在父组件中定义方法:bind:handleselect或者也可以直接写成bindhandleselect*/<cart-item commodity="{{item}}" bind:handleselect=“handleSelect” />在子组件中调用this.triggerEvent(‘handleselect’, { commodity, selectedNum})这个this.triggerEvent(‘handleselect’, { commodity, selectedNum })方法中,handleselect的名称要与父组件中引用子组件时绑定的方法名称一样,后面的对象就是传递的值,也可以直接是以直接量的形式传递,然后再父组件中通过e.detail来获取对应的值handleSelect(e) { console.log(e.detail) console.log(e.detail.commodity) console.log(e.detail.selectedNum)}5.calc的注意事项我以前也遇到过,然后现在再用的时候,一时间把这点给忘了,在看到编译器样式的时候,才猛然想起.user-content{ padding: 10px 0 10px 50px; width: calc(100% - 50px); /* 计算宽度,’+‘或’-‘符号前后有空格 / height: 18px;}css中使用calc可以进行简单的运算:单位可以是百分比,px,rem,em等单位使用"+","-","","/“运算符(使用”+“或者”-“符号时,符号前后必须加上空格)在Firefox浏览器上使用要加上-moz前缀chrome浏览器上使用要加上-webkit前缀(使用”+“或者”-“符号时,符号前后必须加上空格)6.部分想法其实在样式上还是挺快就完成了,就是在计算商品价格的时候,想了挺久在计算价格时,当时就有点蒙圈,总是想着要怎么判断他是增加数量还是减少数量,然后就陷入死循环的之中。其实不用想她是增加还是减少数量,因为你都是传的是商品的数量,而且在计算时,也是判断了商品是否选中,所以,直接点,计算价格乘以数量就可以了然后选中的商品数量的统计就和计算价格的思路是一样的了正在努力学习中,若对你的学习有帮助,留下你的印记呗(点个赞咯^_^)往期好文推荐:判断iOS和Android及PC端css实现波浪线及立方体微信小程序中遇到的多规格问题(一)实现单行及多行文字省略号 ...

November 13, 2018 · 5 min · jiezi

vue组件从开发到发布

组件化是前端开发非常重要的一部分,从业务中解耦出来,可以提高项目的代码复用率。更重要的是我们还可以打包发布,俗话说集体的力量是伟大的,正因为有许许多多的开源贡献者,才有了现在的世界。不想造轮子的工程师,当不了合格的搬运工 。让我们来了解一下vue组件从开发到打包发布流程,并配置Github主页。本文以 vue-clock2 组件为例,欢迎star^_^~~ 项目地址目标框架:vue打包工具:webpack发布源:npm代码托管:github项目结构|– node_modules|– src| |– index.js| |– vue-clock.vue|– docs| |– index.html| |– index.css|– distsrc: 组件相关代码。node_modules: 组件依赖包。docs: 说明文档,组件简单的可以单个页面,也可以使用vuepress。dist: 打包后组件内容,一般 package.json 的 main 入口指向这个文件夹里的文件。组件开发vue组件开发相对来讲还是比较容易的,创建一个 vue-clock.vue 文件,组件的相关逻辑实现。该组件主要实现一个基于 time 属性输入,显示对应时间的钟表样式。 <div class=“clock”> <div class=“clock-circle”></div> <div class=“clock-hour” :style="{transform:hourRotate}"></div> <div class=“clock-minute” :style="{transform:minuteRotate}"></div> <b class=“hour” v-for=“h in timeList” :key=“h”> <span>{{h}}</span> </b> </div>通过元素画出钟表的样式,基于 css3的transform 属性旋转出每个时间点。因为钟表的时针并不是直接跳到下一个点的,所以需要计算出不同分钟时,时钟指针的旋转角度。后续增加了不指定时间的情况,显示当前时间并每分钟自动更新。export default { data() { return { timeList: [12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], hourRotate: “rotatez(0deg)”, minuteRotate: “rotatez(0deg)” }; }, props: [“time”], watch: { time() { this.show(); } }, methods: { show() { this.showTime(); if (this._timer) clearInterval(this._timer); if (!this.time) { this._timer = setInterval(() => { this.showTime(); }, 60 * 1000); } }, showTime() { let times; if (this.time) { times = this.time.split(":"); } else { const now = new Date(); times = [now.getHours(), now.getMinutes()]; } let hour = +times[0]; hour = hour > 11 ? hour - 12 : hour; let minute = +times[1]; let hourAngle = hour * 30 + minute * 6 / 360 * 30; let minuteAngle = minute * 6; this.hourRotate = rotatez(${hourAngle}deg); this.minuteRotate = rotatez(${minuteAngle}deg); } }, mounted() { this.show(); }, destroyed() { if (this._timer) clearInterval(this._timer); }};还有一些钟表的布局样式,可以直接在项目里查看。vue-clock.vue接着我们需要抛出组件,以便在项目中引入使用。 // src/index.js import Clock from ‘./vue-clock.vue’; export default Clock; if (typeof window !== ‘undefined’ && window.Vue) { window.Vue.component(‘clock’, Clock); }这里,组件开发的部分已经完成了,喝杯咖啡,check一下代码,我们要把它打包发布到npm上。打包发布打包前确认一下 webpack 的配置文件输出。 output: { path: path.resolve(__dirname, ‘./dist’), publicPath: ‘/dist/’, filename: ‘vue-clock.min.js’, library: ‘Clock’, libraryTarget: ‘umd’, umdNamedDefine: true }打包组件文件到 dist 文件夹中。npm run buildnpm发布配置package.json{ “name”: “vue-clock2”, “description”: “Vue component with clock”, “version”: “1.1.2”, “author”: “bestvist”, “keywords”: [ “vue”, “component”, “clock”, “time” ], “main”: “dist/vue-clock.min.js”, “license”: “MIT”, “homepage”: “https://bestvist.github.io/vue-clock2/"}登录npm如果使用淘宝镜像的,需要先修正一下镜像源。npm config set registry https://registry.npmjs.org/// 查看登录人npm whoami// 登录npm login// 发布npm publish如果看到类似信息,说明发布成功。npm notice+ vue-clock2@1.1.2Github主页把项目上传到github托管,配置一份基本 README.md 说明文档。因为组件已经发布到npm上,所以可以配置几个徽章在README中。// npm 版本npm version// npm 下载量npm download更多的徽章配置可以查看shields接着描述一下组件的引入和使用方法:安装:npm install vue-clock2使用:<template> <clock :time=“time”></clock></template><script> import Clock from ‘vue-clock2’; export default { components: { Clock }, data () { return { time: ‘10:40’ } } }</script>更详细的交互或是属性说明就交给文档来解决了。在 github 项目上通过 settings 指定 GitHub Pages组件文档说明应包括:组件引入方法组件使用方法一个简单的例子组件属性描述说明总结开发 -> 发布 -> 托管一个组件轮子的制作流程大致介绍完了,希望本文可以帮助到您。原文链接 ...

October 16, 2018 · 2 min · jiezi

vue 时钟组件

推荐一个时钟组件,可自动更新时间。欢迎star????????GitHub地址安装npm install vue-clock2例子<template> <clock :time=“time”></clock></template><script> import Clock from ‘vue-clock2’; export default { components: { Clock }, data () { return { time: ‘10:40’ } } }</script>

October 12, 2018 · 1 min · jiezi