阿秋_在路上

阿秋_在路上 查看完整档案

深圳编辑  |  填写毕业院校货拉拉  |  高级前端工程师 编辑填写个人主网站
编辑

前端工程师

个人动态

阿秋_在路上 发布了文章 · 10月19日

Vue以及React都要会的JSX使用及源码总结😮

背景

  学习前端的同学们应该都知道React和Vue在国内属于最热门的框架或者库。两者之间有许多相似,比如它们都有:

  • 使用虚拟DOM(Virtual DOM)
  • 提供了响应式 (Reactive) 和组件化 (Composable) 的视图组件
  • 将注意力集中保持在核心库,而将其他功能如路由和全局状态管理等交给相关的库

  但是又不一样,Vue是一个渐进式的Javascript框架,特点是易用,灵活,高效。React是一个用于构建用户界面的JavaScript库,特点是以声明式编写 UI,组件化,一次学习,随处编写。当然这里不是来说两者的区别的,只是为来引出在开发过程中比较直观的不一样那就是视图的编写方式。


JSX在React与Vue中的正确姿势

  React的项目中,一切都是JavaScript。HTML可以用JSX来编写,但是实际上JSX与HTML毛关系也没有,JSX实际是一种语法糖,最终还是会转换成JavaScript代码。但是JSX带来的优势却很明显,比如:

  • 你可以使用JavaScript来构建你的视图页面。

     //组件保存的变量名首字母必须大写,否则无法渲染成功
     // React DOM使用camelCase(小驼峰)命名来定义属性的名称,而不是使用HTML的属性名称
     //使用临时变量、JS自带的流程控制、以及直接引用当前 JS 作用域中的值等。
     const demoReact.createClass({
       render: function() {
         var dataArr = [1,2,3,4],
             jsxDomList = [];
          //临时变量 
         dataArr.forEach(function(item, index) {
            //js控制
            if(index < 4){
               //class 需要改为 className
               //大括号之之间只能写表达式(逻辑运算符(||、&&等)以及三目运算符而不能写判断等语句
              jsxDomList.push(<li className="item">{item}</li>);
            }
         });
         //事件绑定 内嵌样式
         return (<ul 
             onClick={this.handlerClick} 
           style={{width:30,height:30,backgroundColor:'red'}>
               {jsxDomList}
           </ul>);
       } 
       handlerClick: function() {
           alert('点了一下');
       }
    });
    ReactDOM.render(<HelloMessage name="John" />,document.getElementById('example'));
  • 开发工具对JSX的支持比现有可用的其他Vue模板更先进一些 (比如linting、类型检查、编辑器的自动完成)(强大生态的支持)

  Vue推荐的方式是使用template来创建我们的HTML,我相信日常开发中99%的前端用的都是这种方式。事实上,vue也提供了渲染函数,支持JSX语法。但是语法却不同。

Vue 选项中的 render 函数若存在,则 Vue 构造函数不会从 template 选项或通过 el 选项指定的挂载元素中提取出的 HTML 模板编译渲染函数。

  React中父子之间传递的所有数据都是属性,即所有数据均挂载在props下(style, className, children, value, onChange等等)。Vue则是属性就有三种:组件属性props,普通html属性attrs,Dom属性domProps。

  • React
var reactElement = ReactElement.createElement(
      'demo', // 标签名称字符串/ReactClass
      {
      id: "title",
      className: "bg",
      style: {
        color: 'red'
      }
    } // {元素的属性值对对象}或者是null
      sth... // [元素的子节点] 子节点数组 也可不传
)
  • Vue2.0
return createElement('div', {
  // 与 `v-bind:class` 的 API 相同
  'class': {
    foo: true,
  },

  // 与 `v-bind:style` 的 API 相同,
  // 接受一个字符串、对象,或对象组成的数组
  style: {
    color: 'blue',
    fontSize: '14px'
  },

  // 普通的 HTML 特性
  attrs: {
    id: 'foo'
  },

  // 组件 prop
  props: {
    myProp: 'bar'
  },
  // DOM 属性
  domProps: {
    innerHTML: 'eg'
  },
  // 事件监听器在 `on` 属性内,
  // 但不再支持如 `v-on:keyup.enter` 这样的修饰器。
  // 需要在处理函数中手动检查 keyCode。
  on: {
    click: this.clickHandler
  },

  // 仅用于组件,用于监听原生事件,而不是组件内部使用
  // `vm.$emit` 触发的事件。
  nativeOn: {
    click: this.nativeClickHandler
  },

  // 自定义指令。注意,你无法对 `binding` 中的 `oldValue`
  // 赋值,因为 Vue 已经自动为你进行了同步
  // 比如常见的权限管理指令
  directives: [
    {
      name: 'permissions',
      value: {
        currentPermissions:
          'download_csv'
      }
    }
  ],

  // 作用域插槽的格式为
  // { name: props => VNode | Array<VNode> }
  scopedSlots: {
    default: props => createElement('span', props.text)
  },

  // 如果组件是其它组件的子组件,需为插槽指定名称
  slot: 'name-of-slot',
  // 其它特殊顶层属性
  key: 'myKey',
  ref: 'myRef',
  // 如果你在渲染函数中给多个元素都应用了相同的 ref 名,
  // 那么 `$refs.myRef` 会变成一个数组。
  refInFor: true
})
  • Vue3.0
//Vue3.0 官方例子
const app = Vue.createApp({})
app.component('anchored-heading', {
  render() {
    const { h } = Vue
    return h(
      'h' + this.level, //标签名称字符串
      {}, // props/attributes
      this.$slots.default() //子节点数组 也可不传
    )
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})
//main.js
app.directive('demo', (el, binding) => {
  console.log(binding.value.color) // => "white"
  console.log(binding.value.text) // => "hello!"
})
//component
setup(props, { slots, emit }) {
    //响应值
    const count = ref(1)
    //使用指令
    const vDemo = resolveDirective('demo')
    return ()=>{
      const dom = h('a',{
        class: ['wrap'],
        style: {
          zIndex: count.value,
        },
        onClick: ()=>{
          console.log('点了一下1');
        },
        onMouseover: ()=>{
          console.log('Mouseover了一下');
        },
        onUpdate:()=>{
          console.log('update');
        },
        // 正常的 HTML 特性
        href:'www.baidu.com',
        key: 'myKey',
        ref: 'myRef'
      },[
        h('span',null,123)
      ])
      console.log(dom);
      return withDirectives(dom,[[vDemo, { color: 'white', text: 'hello!' }]])
    }
  },

控制台打印

react的createElement源码

function createElement(type, config, children) {       
      var propName;
      //提取保留名称
      var props = {};
      var key = null;
      var ref = null;
      var self = null;
      var source = null;
      //标签的属性不为空时 说明标签有属性值 特殊处理:把key和ref赋值给单独的变量
      if (config != null) {
        //有合理的ref
        if (hasValidRef(config)) {
          ref = config.ref;
        }
        //有合理的key 
        if (hasValidKey(config)) {
          key = '' + config.key;
        }

        self = config.__self === undefined ? null : config.__self;
        source = config.__source === undefined ? null : config.__source;
        //config中剩余属性,且不是原生属性(RESERVED_PROPS对象的属性),则添加到新props对象中
        for (propName in config) {
          if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
            //config去除key/ref 其他属性的放到props对象中
            props[propName] = config[propName]; 
          }
        }
      }
      //子元素数量(第三个参数以及之后参数都是子元素 兄弟节点)
      var childrenLength = arguments.length - 2; 
      if (childrenLength === 1) {
        props.children = children;
      } else if (childrenLength > 1) {
        //声明一个数组
        var childArray = Array(childrenLength); 
        //依次将children push到数组中
        for (var i = 0; i < childrenLength; i++) {
          childArray[i] = arguments[i + 2];
        }

        {
          //冻结array 返回原来的childArray且不能被修改 防止有人修改库的核心对象 冻结对象大大提高性能
          if (Object.freeze) {
            Object.freeze(childArray);
          }
        }
        //父组件内部通过this.props.children获取子组件的值
        props.children = childArray; 
      } 

      //为子组件设置默认值 一般针对的是组件      
      //class com extends React.component 则com.defaultProps获取当前组件自己的静态方法 
      if (type && type.defaultProps) { 
         //如果当前组件中有默认的defaultProps则把当前组件的默认内容 定义到defaultProps中
        var defaultProps = type.defaultProps; 

        for (propName in defaultProps) { 
          if (props[propName] === undefined) { 
            //如果父组件中对应的值为undefinde 则把默认值赋值赋值给props当作props的属性
            props[propName] = defaultProps[propName];
          }
        }
      }

      {
        //一旦ref或者key存在
        if (key || ref) {
          //如果type是组件的话
          var displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type;
          if (key) {
            defineKeyPropWarningGetter(props, displayName);
          }

          if (ref) {
            defineRefPropWarningGetter(props, displayName);
          }
        }
      }

      //props:
      // 1.config的属性值
       // 2.children的属性(字符串/数组)
       //3.default的属性值
      return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);
    }

Vue3.0的实现JSX的源码

function _createVNode(type, props = null, children = null, patchFlag = 0, dynamicProps = null, isBlockNode = false) {
    //传入非法type
    if (!type || type === NULL_DYNAMIC_COMPONENT) {
        if ( !type) {
            warn(`Invalid vnode type when creating vnode: ${type}.`);
        }
        type = Comment;
    }
    if (isVNode(type)) {
        // createVNode接收到已经存在的vNode 比如:<component :is="vnode"/>
        // 确保在克隆期间合并refs,而不是覆盖它
        const cloned = cloneVNode(type, props, true /* mergeRef: true */);
        if (children) {
            normalizeChildren(cloned, children);
        }
        return cloned;
    }
    // 类组件标准化
    if (isClassComponent(type)) {
        type = type.__vccOpts;
    }
    // class & style处理
    if (props) {
        //对于reactive或proxy对象,我们需要克隆它以启用
        if (reactivity.isProxy(props) || InternalObjectKey in props) {
            props = shared.extend({}, props);
        }
        let { class: klass, style } = props;
        if (klass && !shared.isString(klass)) {
            props.class = shared.normalizeClass(klass);
        }
        if (shared.isObject(style)) {
            //reactive对象需要克隆,因为它们可能会发生突变
            if (reactivity.isProxy(style) && !shared.isArray(style)) {
                style = shared.extend({}, style);
            }
            props.style = shared.normalizeStyle(style);
        }
    }
    // 将vnode类型信息编码
    const shapeFlag = shared.isString(type)
        ? 1 /* ELEMENT */
        :  isSuspense(type)
            ? 128 /* SUSPENSE */
            : isTeleport(type)
                ? 64 /* TELEPORT */
                : shared.isObject(type)
                    ? 4 /* STATEFUL_COMPONENT */
                    : shared.isFunction(type)
                        ? 2 /* FUNCTIONAL_COMPONENT */
                        : 0;
    if ( shapeFlag & 4 /* STATEFUL_COMPONENT */ && reactivity.isProxy(type)) {
        type = reactivity.toRaw(type);
        warn(`Vue received a Component which was made a reactive object. This can ` +
            `lead to unnecessary performance overhead, and should be avoided by ` +
            `marking the component with \`markRaw\` or using \`shallowRef\` ` +
            `instead of \`ref\`.`, `\nComponent that was made reactive: `, type);
    }
    //vNode 包含的值
    const vnode = {
        __v_isVNode: true,
        ["__v_skip" /* SKIP */]: true,
        type,
        props,
        key: props && normalizeKey(props),
        ref: props && normalizeRef(props),
        scopeId: currentScopeId,
        children: null,
        component: null,
        suspense: null,
        ssContent: null,
        ssFallback: null,
        dirs: null,
        transition: null,
        el: null,
        anchor: null,
        target: null,
        targetAnchor: null,
        staticCount: 0,
        shapeFlag,
        patchFlag,
        dynamicProps,
        dynamicChildren: null,
        appContext: null
    };
    // 验证key值,不能为NaN
    if ( vnode.key !== vnode.key) {
        warn(`VNode created with invalid key (NaN). VNode type:`, vnode.type);
    }
    normalizeChildren(vnode, children);
    // normalize suspense children
    if ( shapeFlag & 128 /* SUSPENSE */) {
        const { content, fallback } = normalizeSuspenseChildren(vnode);
        vnode.ssContent = content;
        vnode.ssFallback = fallback;
    }
    if (shouldTrack > 0 &&
        // 避免块节点跟踪自己
        !isBlockNode &&
        // 有当前父块
        currentBlock &&
        //组件节点也应该总是被修补,因为即使组件不需要更新,它也需要将实例持久化到下一个vnode上,以便以后可以正确地卸载它。
        (patchFlag > 0 || shapeFlag & 6 /* COMPONENT */) &&
        //事件标志只是为了hydration
        //如果它是唯一的标志,vnode不应该被认为是动态的,因为处理程序缓存
        patchFlag !== 32 /* HYDRATE_EVENTS */) {
        currentBlock.push(vnode);
    }
  

最后

  在vue中JSX的使用场景还是很多的,比如在大多的ui框架中,都有table的渲染,如果框架通过传入对象数据,然后使用JSX渲染出来,其灵活性就会很高了。比如View UI,即原先的 iView而且不管是React还是Vue都是为了提高开发者的生产力,没有谁好,谁不好。只有在不同的生产环境中,谁更加合适。

  如果有想了解更多的Vue3.0可以查看vue3.0学习

   最后一句,喜欢的点赞支持!

查看原文

赞 0 收藏 0 评论 0

阿秋_在路上 发布了文章 · 10月13日

来来来~Async Validator源码看一下

背景

在使用ivew.design的时候,在源码中发现form表单的验证是使用Async Validator,然后就去看一下源码,了解原理并做一下整理。

const validator = new AsyncValidator(descriptor);
let model = {};
model[this.prop] = this.fieldValue;
validator.validate(model, { firstFields: true }, errors => {
    this.validateState = !errors ? 'success' : 'error';
    this.validateMessage = errors ? errors[0].message : '';
    callback(this.validateMessage);
    this.FormInstance && this.FormInstance.$emit('on-validate', this.prop, !errors, this.validateMessage || null);
});

这个是源码的版本


先了解咋用

  • 安装

    npm i async-validator
  • 用法

    更多的细节就从源码里面去说明了,说多了也记不得~


总体解析

  1. 大致设计如下
  2. 最后导出的Schema构造函数

    由上面可以看出,这个js库最后导出Schema构造函数,并且分成几个大块,原型链上面的方法,register,warning,messages,validators

warning方法

在实例化 Schema 之前设置 warning 方法。该方法实际上是util.js文件里面的一个工具方法

如果放弃所有的控制太警告,你可以自己初始化
Schema.warning = () => {}
//默认是一个空函数,这样if条件没有通过时将不会在控制台打印警告
export let warning = () => {};
// 在生产环境或者node runtime时,不打印警告信息
if (
  typeof process !== 'undefined' &&
  process.env &&
  process.env.NODE_ENV !== 'production' &&
  typeof window !== 'undefined' &&
  typeof document !== 'undefined'
) {
  warning = (type, errors) => {
    if (typeof console !== 'undefined' && console.warn) {
      if (errors.every(e => typeof e === 'string')) {
        //实际上就是一个console.warn
        console.warn(type, errors);
      }
    }
  };
}

messages方法

Schema 中的message方法实际上是message.js文件中导出的实例,是根据不同类型的失败校验的提示用的消息模板.

//index.js
//构造函数
 function Schema(descriptor) {
  //descriptor声明了校验规则
  this.rules = null;
  //使用私有属性_messages保存defaultMessages
  this._messages = defaultMessages;
  this.define(descriptor);
}
Schema.messages = defaultMessages;
//message.js
export function newMessages() {
  return {
    default: 'Validation error on field %s',
    required: '%s is required',
    enum: '%s must be one of %s',
    whitespace: '%s cannot be empty',
    date: {
      format: '%s date %s is invalid for format %s',
      parse: '%s date could not be parsed, %s is invalid ',
      invalid: '%s date %s is invalid',
    },
    types: {
      string: '%s is not a %s',
      method: '%s is not a %s (function)',
      array: '%s is not an %s',
      object: '%s is not an %s',
      number: '%s is not a %s',
      date: '%s is not a %s',
      boolean: '%s is not a %s',
      integer: '%s is not an %s',
      float: '%s is not a %s',
      regexp: '%s is not a valid %s',
      email: '%s is not a valid %s',
      url: '%s is not a valid %s',
      hex: '%s is not a valid %s',
    },
    string: {
      len: '%s must be exactly %s characters',
      min: '%s must be at least %s characters',
      max: '%s cannot be longer than %s characters',
      range: '%s must be between %s and %s characters',
    },
    number: {
      len: '%s must equal %s',
      min: '%s cannot be less than %s',
      max: '%s cannot be greater than %s',
      range: '%s must be between %s and %s',
    },
    array: {
      len: '%s must be exactly %s in length',
      min: '%s cannot be less than %s in length',
      max: '%s cannot be greater than %s in length',
      range: '%s must be between %s and %s in length',
    },
    pattern: {
      mismatch: '%s value %s does not match pattern %s',
    },
    clone() {
      // 深拷贝
      const cloned = JSON.parse(JSON.stringify(this));
      cloned.clone = this.clone;
      return cloned;
    },
  };
}
export const messages = newMessages();

当然有必要的话,这个messages的模板自己也是可以改造的。

//index.js
//Schema.prototype的原型方法中有message的方法
messages(messages) {
  if (messages) {
      //将 _messages和参数 深度合并合并
    this._messages = deepMerge(newMessages(), messages);
  }
  return this._messages;
}
//util.js
//deepMerge是util.js中的一个普通合并对象的方法,先是遍历一遍对象,然后对下一层的对象使用结构,实际上只有2层的合并
export function deepMerge(target, source) {
  if (source) {
    for (const s in source) {
      if (source.hasOwnProperty(s)) {
        const value = source[s];
        if (typeof value === 'object' && typeof target[s] === 'object') {
          target[s] = {
            ...target[s],
            ...value,
          };
        } else {
          target[s] = value;
        }
      }
    }
  }
  return target;
}
//gitHub给到的例子
import Schema from 'async-validator';
const cn = {
  required: '%s 必填',
};
const descriptor = { name: { type: 'string', required: true } };
const validator = new Schema(descriptor);
// 将cn与defaultMessages深层合并
validator.messages(cn);
...

validators

  1. 为用户提供的各种数据类型的验证方法
import validators from './validator/index';
Schema.validators = validators;
  1. 以对string类型的判断为例

    • rule: 在源descriptor中,与要校验的字段名称相对应的校验规则。始终为它分配一个field属性,其中包含要验证的字段的名称。
//这个样子
{
    [field: string]: RuleItem | RuleItem[]
}
//例子
{name:{type: "string", required: true, message: "Name is required"}}
  • value: 源对象属性中要校验的值。
  • callback: 校验完成后需要调用的callback。传递一个Error实例数组以判断校验失败。如果校验是同步的,则可以直接返回false、Error或Error Array
callback(errors)
  • source: 传给validate 方法的源对象
  • options: 额外选项
//options的内部属性
export interface ValidateOption {
 // 是否取消关于无效值的内部警告
 suppressWarning?: boolean;
 // 当第一个验证规则生成错误时,停止处理
 first?: boolean;
 //当指定字段的第一个验证规则生成错误时,停止处理字段,“true”表示所有字段。
 firstFields?: boolean | string[];
}
  • options.messages: 包含校验 error message 的对象,将与 defaultMessages 进行深度合并
//所有的验证的方法都是以rule, value, callback, source, options为参数
function string(rule, value, callback, source, options) {
  // 需要callback出去的错误列表
  const errors = [];
  //首先验证required为false或者还未填写状态就直接返回
  const validate =
    rule.required || (!rule.required && source.hasOwnProperty(rule.field));
  if (validate) {
    //isEmptyValue判断是否为空值,并且将空数组也判断为true
    if (isEmptyValue(value, 'string') && !rule.required) {
      return callback();
    }
    //使用rules方法判断是否必填
    rules.required(rule, value, source, errors, options, 'string');
    if (!isEmptyValue(value, 'string')) {
      // 判断type,range范围(这里有涉及了len,min,max判断),以及提供正则表达式的判断
      rules.type(rule, value, source, errors, options);
      rules.range(rule, value, source, errors, options);
      rules.pattern(rule, value, source, errors, options);
      if (rule.whitespace === true) {
          //为仅由空格组成的字符串添加额外的校验
        rules.whitespace(rule, value, source, errors, options);
      }
    }
  }
  callback(errors);
}

export default string;

register

除了上述提供的validators的方法,这个register用于自定义判断

Schema.register = function register(type, validator) {
  //必须是函数
  if (typeof validator !== 'function') {
    throw new Error(
      'Cannot register a validator by type, validator is not a function',
    );
  }
  validators[type] = validator;
};

为validators验证的方法提供的rule

/**
 *  所有的方法的传值如下
 *  @param rule 校验的规则
 *  @param value source对象中该字段的值
 *  @param source 要校验的source对象
 *  @param errors 本次校验将要去添加的errors数组
 *  @param options 校验选项
 *  @param options.messages 校验的messages
 */
export default {
  required, //属性为必填
  whitespace, //校验空白字符
  type, //判断type属性
  range, //通过传入的number类型的len,min,max 进行判断
  enum: enumRule,//校验值是否存在在枚举值列表中
  pattern,//校验是否符合校验正则表达式
};

type有如下类型

const custom = [
    'integer',
    'float',
    'array',
    'regexp',
    'object',
    'method',
    'email',
    'number',
    'date',
    'url',
    'hex',
  ];
// 枚举验证 官方案例
const descriptor = {
  role: { type: 'enum', enum: ['admin', 'user', 'guest'] },
};

原型链上面的方法

  1. messages
//上文有交代,用于新增自己的错误消息提示模板
 messages(messages) {
  if (messages) {
    this._messages = deepMerge(newMessages(), messages);
  }
  return this._messages;
},
  1. define

    注册校验规则rules

  1. getType

    通过此方法获得rule的type,并会判断此type是否是已经定义好的validators中的type

  2. getValidationMethod

通过此方法获得rule的validator

  getValidationMethod(rule) {
    //定义好了validator直接返回
    if (typeof rule.validator === 'function') {
      return rule.validator;
    }
    //收集单个rule里面定义的所有key
    const keys = Object.keys(rule);
    const messageIndex = keys.indexOf('message');
    if (messageIndex !== -1) {
      //删除message属性
      keys.splice(messageIndex, 1);
    }
    //除了message,就只有一个key且是required,返回validators提供的required方法
    if (keys.length === 1 && keys[0] === 'required') {
      return validators.required;
    }
    //否则,最后一种情况,根据rule定义的type判断
    // 如果type是非法未定义的type则直接返回false
    return validators[this.getType(rule)] || false;
  },
  1. validate
核心方法
  1. 参数:
   source_ 需要校验的对象
   o  即options 描述校验的处理选项的对象(上文说的,suppressWarning,firstFields,first)
   oc 即callback 校验完成后的回调函数
  2. 返回值是一个Promise对象:
    then 校验通过
    catch({errors,fields}) 校验失败
    errors是一个所有error数组,fiels是一个类似{field1:[error,error...],field2:[error,error...]}的对象

不得不说asyncMap又是怎么内容
首先它。若options.first为否值,;再根据options.firstFields是否为真值,分别执行asyncSerialArray、asyncParallelArray函数。

export function asyncMap(objArr, option, func, callback) {
 //判断first
  if (option.first) {
      //判断options.first是否为真值,若为真值,调用asyncSerialArray处理series数组
    //当某一规则校验失败时,即终止校验,执行callback回调
    const pending = new Promise((resolve, reject) => {
      const next = errors => {
        callback(errors);
        return errors.length
          ? reject(new AsyncValidationError(errors, convertFieldsError(errors)))
          : resolve();
      };
      const flattenArr = flattenObjArr(objArr);
      asyncSerialArray(flattenArr, func, next);
    });
    pending.catch(e => e);
    return pending;
  }
  
  let firstFields = option.firstFields || [];
  if (firstFields === true) {
    firstFields = Object.keys(objArr);
  }
  const objArrKeys = Object.keys(objArr);
  const objArrLength = objArrKeys.length;
  let total = 0;
  const results = [];
  const pending = new Promise((resolve, reject) => {
    //构建next函数包装callback,目的是将所有校验器的失败文案合并再传入callback
    const next = errors => {
      results.push.apply(results, errors);
      total++;
      if (total === objArrLength) {
        callback(results);
        return results.length
          ? reject(
              new AsyncValidationError(results, convertFieldsError(results)),
            )
          : resolve();
      }
    };
    if (!objArrKeys.length) {
      callback(results);
      resolve();
    }
    objArrKeys.forEach(key => {
      const arr = objArr[key];
      //判断firstFields值,分别执行asyncSerialArray、asyncParallelArray函数
      //asyncParallelArray函数用于实现平行校验,在某个异步校验器执行过程中,平行调用下一个校验器;
      //asyncSerialArray用于实现有序校验,在异步校验器执行完成后,再启用下一个校验器。
      if (firstFields.indexOf(key) !== -1) {
        asyncSerialArray(arr, func, next);
      } else {
        asyncParallelArray(arr, func, next);
      }
    });
  });
  pending.catch(e => e);
  return pending;
}

最后

就这样了,有新的了解,后面再编辑,修改

喜欢,帮忙点赞👍

查看原文

赞 0 收藏 0 评论 0

阿秋_在路上 发布了文章 · 9月25日

尝鲜vue3.0- one Piece发布后的新鲜事(6)

背景

延续前面的五篇文章:

  上周六晚上,vue3.0正式发布。本以为距离正式发布还需要一段日子,突然就发了正式版本“One Piece”。
  vue3.0最新的release地址
  看完之后,发现了一个新东西😨lit-html

  再去尤大佬的github上面Look了一眼,看见了一个新的小工具vue/lit


vue/lit源码

  • 首先,这一个@vue/reactivitylit-html结合在一起的迷你版自定义元素框架(不满70行代码)
  • 源码都干啥了

customElements.define是啥

  自定义元素是能够创建将功能封装在HTML页面上的自定义元素,而不必使用一长批嵌套的元素,是Web Components标准的关键特性之一<br/>  customElements是CustomElementRegistry的别名<br/>  API在MDN地址

//html
<div id="content"></div>
<div is="button-hello">123</div>


lit-html是啥

可以在 javascript 中写的 html 模版引擎
<br/>lit-html官网地址
<br/> 表面上看很像jsx ,但是这仅是 JavaScript 的语法。lit-html并没有虚拟 dom 的概念也没有 diff 检查,更专注做为渲染引擎,不提供组件、应用框架的东西,因此,概念极少,基本上就一个构造 html 模板,然后就是render。因此大佬开发vue-lit,尝试将两者结合,为vue用户提供其他的渲染引擎。
<br/>特点:

  • 可以在 javascript 中写的 html 模板
  • 快速启动和更新,不需要构建工具预编译、预处理(不高的浏览器兼容性,面向以后的技术)
  • 体积小
  • 上手easy,API扩展性强
// Import lit-html
import {html, render} from 'lit-html';

// Define a template
const myTemplate = (name) => html`<p>Hello ${name}</p>`;

// Render the template to the document
render(myTemplate('World'), document.body);

最后

vue3.0到这里算是尝鲜完毕,赶紧学起来吧。
喜欢就点赞哟,溜啦溜啦!
github代码地址

![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/31398db0bb02435d9e941ea899540f8a~tplv-k3u1fbpfcp-zoom-1.image)
查看原文

赞 0 收藏 0 评论 0

阿秋_在路上 发布了文章 · 9月25日

尝鲜vue3.0- 扶我起来学一下组合式API(5)

背景

延续前面的四篇文章:

看完上面其实可以自己开发vue3.0了,但是这里介绍一下vue组合式API,东西不多


setup

  • 作为在组件内使用 Composition API 的入口点
  • 传入参数:props,{slot,emit,attr}
  • 时机:创建组件实例=>初始化props=>调用setup(在beforeCreate钩子之前调用)
  • setup 返回的 ref 在模板中会自动解开,不需要写 .value
  • this在setup函数中不可用
// html
//点击count+1
<template>
  <div class="demo">
    <span @click="addCount">{{count}}</span>
  </div>
</template>
//ts
<script lang="ts">
import {ref} from 'vue'
export default {
  name: 'demo',
  setup(props,{slot,emit,attr}){
    const count = ref(0)
    const addCount = ()=>{
      count.value++
    }
    return {count,addCount}
  }
}
</script>
  • 相同逻辑jsx写法(demo.vue组件)

  • 相同逻辑使用render API(demo.vue组件)

  • defineComponent(demo.ts组件)

响应式系统API

  • reactive与ref

    • reactive接收一个普通对象然后返回该普通对象的响应式代理,类似2.0的Vue.observable()
    • ref接受一个参数值并返回一个响应式且可改变的 ref 对象
    • 打印ref对象和reactive对象
  • computed

     const count = ref(1)
     //也可只传入一个 const plusOne = computed(() => count.value + 1)
     //传入get或者set属性,
     const plusOne = computed({
       get: () => count.value + 1,
       set: (val) => {
         count.value = val - 1
       },
     })
  • readonly(传入一个对象(响应式或普通)或 ref,返回一个原始对象的只读代理)

  • watchEffect

    • 立即执行传入的一个函数,并响应式追踪其依赖,并在其依赖变更时重新运行该函数。

      //初始化运行是在组件 mounted 之前执行的 初始化时就会执行一次 建议写在onMounted钩子内
       watchEffect(() => console.log(count.value))
    • 可传入option

        interface WatchEffectOptions {
         //设置function需要同步或在组件更新之前重新运行,默认post
         flush?: 'pre' | 'post' | 'sync' 
         //onTrack 和 onTrigger 选项可用于调试一个debugger的行为。
         //当一个 reactive 对象属性或一个 ref 作为依赖被追踪时,将调用 onTrack
         //依赖项变更导致副作用被触发时,将调用 onTrigger
         onTrack?: (event: DebuggerEvent) => void
         onTrigger?: (event: DebuggerEvent) => void
       }
    • 停止监听行为和停止回调

       const stop = watchEffect((onInvalidate) => {
           console.log(count.value)
           onInvalidate(()=>{
               console.log('watchEffect结束')
           })
       })
       stop() //停止
  • watch

       //与watchEffect相比
       1. 懒执行回调函数
       2. 更明确哪些状态的改变会触发侦听器重新运行副作用
       3. 访问侦听状态变化前后的值
       4. 初始化时就不会执行

生命周期钩子

2.03.0
beforeCreate使用 setup()
created使用 setup()
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeDestroyonBeforeUnmount
destroyedonUnmounted
errorCapturedonErrorCaptured

依赖注入

功能类似 2.x 的 provide/inject,只能在setup中调用

// 两者之间可以相互响应
// 提供者:
const themeRef = ref('dark')
provide(ThemeSymbol, themeRef)

// 使用者:
const theme = inject(ThemeSymbol, ref('light'))
watchEffect(() => {
  console.log(`theme set to: ${theme.value}`)
})

模版refs

类似在vue2.0里面的this.$refs,但是用法已经变了

    //html
   <div class="demo" ref="demo">
     <span @click="addCount">{{count}}--{{countObject.count}}</span>
   </div>
    //setup
    const demo = ref(null)
   onMounted(()=>{
     //只在渲染初始化后才能访问。
     console.log(demo.value)
   })
// 获取list的refs
 <div v-for="(item, i) in list" :ref="el => { divs[i] = el }">
   {{ item }}
 </div>

响应式系统utils

  • isRef与unref

    //isRef 检查一个值是否为一个 ref 对象。
    //unref是val = isRef(val) ? val.value : val 的语法糖
  • toRef与toRefs

    在介绍setup中,props作为参数传入,这里的props不可用解构的方式获取,不然会失去响应性

     // toRef 可以用来为一个 reactive 对象的属性创建一个 ref
     const countObject = reactive({count:0})
     const countinObject = toRef(countObject,'count')
     //props获取单独的数据,并保持响应性
     const propsCount = toRef(props, 'count')
     //toRefs 把响应式对象转化为一个个的ref
     const state = reactive({
       foo: 1,
       bar: 2,
     })
     const stateAsRefs = toRefs(state)
     /*
     stateAsRefs 的类型如下:
    
     {
       foo: Ref<number>,
       bar: Ref<number>
     }
     */
  • isProxy,isReactive,isReadonly

  1. isProxy判断一个对象是否是由 reactive 或者 readonly 方法创建的代理
  2. isReactive判断一个对象是否是由 reactive 创建的响应式代理
  3. isReadonly判断一个对象是否是由 readonly 创建的只读代理


高级响应式系统API

  • customRef:自定义ref,显式地控制依赖追踪和触发响应

  • markRaw与shallowXX

     //markRow与下面的 shallowXXX 这些特性仅停留在根级别(即对象的第一层,深层次的不影响)
     //1. markRaw:标记一个对象为“永远不会转为响应式代理”,函数返回这个对象本身
     const foo = markRaw({})
     console.log(isReactive(reactive(foo))) // false
     // 如果被 markRaw 标记了,即使在响应式对象中作属性,也依然不是响应式的
     const bar = reactive({ foo })
     console.log(isReactive(bar.foo)) // false
     // 2.shallowReactive:只为某个对象的私有(第一层)属性创建浅层的响应式代理
     // 3.shallowReadonly:只为某个对象的自有(第一层)属性创建浅层的只读响应式代理
     // 4.shallowRef:创建一个 ref 
     //注意:shallowRef并不会对变更后的 .value 做响应式代理转换
     const foo = shallowRef({})
     // 更改对操作会触发响应
     foo.value = {}
     // 但上面新赋的这个对象并不会变为响应式对象
     isReactive(foo.value) // false
     //5. toRow 返回由 reactive 或 readonly 方法转换成响应式代理的普通对象

最后

vue3.0到这里算是尝鲜差不错了,喜欢就点赞哟,溜啦溜啦!
github代码地址

查看原文

赞 1 收藏 0 评论 0

阿秋_在路上 发布了文章 · 9月25日

尝鲜vue3.0- 看完就开干(4)

背景

延续前面的三篇文章:

距离正式开发还需要介绍一下路由变化,这样就算对vue3.0开发的以有了初步了解,以及完成项目的基础建设。


安装vue-router

//目前最新版本
npm i --save vue-router@4.0.0-beta.9
"vue-router": "^4.0.0-beta.9"
//配置
import { createRouter, createWebHashHistory } from 'vue-router'

const router = createRouter({
  //createWebHistory() hostory模式
  history: createWebHashHistory(),//hash模式 
  routes: [
    {
      path: '/',
      redirect: '/home',
    },
    // 动态路径参数 以冒号开头
    { path: '/home', component: () => import('../views/home.vue') },
  ],
})
export default router
//使用 main.js
app.use(router)

scoped slot(作用域插槽)

原本是想要 <router-link> 渲染成某种标签,但是使用起来会不够直观清晰

//之前
//tag="button"  router-link将会渲染成button标签
<router-link to="/" tag="button">
    <Icon>home</Icon><span class="xs-hidden">Home</span>
</router-link>
//之后 使用scoped slot替换
<router-link to="/">
  <button role="link">
    <Icon>home</Icon><span class="xs-hidden">Home</span>
  </button>
</router-link>
  • 删除 event属性(vue2.0 router文档event介绍
    默认是'click',声明可以用来触发导航的事件。可以是一个字符串或是一个包含字符串的数组。也被删除,用作用域插槽代替。
  • 停止自动将点击事件分配给内部锚点
  • 新增scoped-slot API
  • 添加custom prop以完全自定义router-link的渲染

  • 简写方式(有坑)

    //从第二篇的地方有介绍过v-slot有简写方式,但是这里值得注意,下方的写法在浏览器渲染却有不通
    <router-link to="/about" #custom="{ href }">
       <a :href="href">1.{{href}}</a>
     </router-link>
     <router-link to="/about" v-slot:custom="{ href }">
       <a :href="href">2.{{href}}</a>
     </router-link>
    
     <router-link to="/about" custom v-slot="{ href }">
       <a :href="href">3.{{href}}</a>
     </router-link>

渲染之后的结果只有第三个正常显示

---
### vue-router的API

和vue3.0一样,api全部走import,不放在this里面(打印 require('vue-router'))


meta属性(给路由附加数据的属性)

{ path: '/my', meta: { requiresAuth: true }}
//之前
router.beforeEach((to, from, next) => {
  if (to.meta.requiresAuth && !auth.loggedIn()) next('/login')
  else next()
})
//建议3.0
router.beforeEach((to, from, next) => {
  if (to.matched.some(record => record.meta.requiresAuth))
  // ...do sth
})

router-link-active/router-link-exact-active class类名

  • 别名和子路由也将激活
  • params不一致不会被激活

假设当前路由/parent/1/child/2

urlactiveexact active
/parent/1/child/2
/parent/1/child/3XX
/parent/1/X

提供可以在路由运作时增删路由记录的API

//my.vue
import {useRouter,useRoute} from 'vue-router' //见上文介绍的vue-router目前的API
setup(props,ctx){
   console.log(useRouter(window.location.pathname));//类似原来的 this.$router
   console.log(useRoute(window.location.pathname));//类似原来的 this.$route
 }
//路由记录的值
const routeRecord: RouteRecord = {
  path: '/new-route',
  name: 'NewRoute',
  component: NewRoute
}
router.addRoute(route: RouteRecord) //新增一个路由记录
router.removeRoute(name: string | symbol) //删除
router.hasRoute(name: string | symbol)://判断是否存在
router.getRoutes(): RouteRecord[] //获取记录列表

路由跳转

  • 明确定义路由跳转失败,然后如何捕获错误
  • 路由跳转将是Promise形式
  • 将router.push与router.afterEach,router.onError保持一致

    router.push('/dashboard').then((failure) => {
      if (failure) {
        failure instanceof Error // true
        failure.type // NavigationFailure.canceled 【aborted,canceled,duplicated】
      }
    }).catch(()=>{...})

同时使用keep-alive与transition

//以前
<transition :name="transitionName" >
  <keep-alive >
    <router-view></router-view>
    <router-view></router-view>
  </keep-alive>
</transition>
//之后
<router-view v-slot="{ Component }">
  <transition :name="transitionName" mode="out-in">
    <component :is="Component"></component>
  </transition>
</router-view>

滚动行为

不常用,vue2.0在官网上的介绍

//改动不大,只是为了更加贴近原生,将属性名统一了
{ selector:..,x: 0, y: 200,behavior }
// becomes
{ el:...,left: 0, top: 200,offset }

在激活某路由的时候增加逻辑判断,将路由视图显示其他内容(比如一些loading状态)

<router-view :route="routeWithModal"></router-view>
const App = {
  computed: {
    routeWithModal() {
      if (backgroundView) {
        return this.$router.resolve(backgroundView)
      } else {
        return this.$route
      }
    }
  }
}

路由守卫不用使用next,也可以直接返回某个值或者是Promise

// 之前
router.beforeEach((to, from, next) => {
  if (!isAuth) next(false)
  else next()
})

// 以后可以
router.beforeEach(() => isAuth)

最后

安装了解完路由的相关变动,直接可以开干了。整个过程下来,vue3.0的变化还是可以接受的,在向更好的方向发展。推荐使用vite也很好,很多东西都内置,开箱即用。至于typesciprt,只是vue3.0更好的支持了ts语法,但是用不用,怎么用还是看项目以及开发者本身。目前介绍vue3.0的使用,到此其实已经可以开始写项目了。还有Vue Composition API没介绍,但是简单和常用的API在上面已经用过了,后期再更新。

github代码地址

介绍到这里,有遗漏欢迎大家补充,喜欢的动手收藏点赞。

查看原文

赞 1 收藏 1 评论 0

阿秋_在路上 发布了文章 · 9月25日

尝鲜vue3.0-tyepscript开发组件(3)

背景

延续前面的两篇文章:


Vite

  1. Vite是一个web开发构建工具,它在开发期间通过本地ES模块导入为您的代码提供服务,并将其与Rollup捆绑在一起用于生产。
  • 闪电般的冷服务器启动
  • 快速热模更换(HMR)
  • 真正的随需应变的编译

实际上就是先启动服务(koa),如果你需要某一个文件,再进行编译。这就造成Vite启动服务的速度比Webpack快了很多,即“按需编译”。

  1. 需要我们注意的是:

    • TypeScript已经内置支持,直接就可以使用
    • less/sass/stylus/postcss也内置支持(单需要单独安装所对应的编译器)
    • git地址
  2. 使用vite初始化项目

      npm init vite-app <project-name>
      cd <project-name>
      npm install
      npm run dev

安装配置

  1. 安装sass,eslint,prettier(注意版本号)


//.eslintrc.js

module.exports = {
  parser: 'vue-eslint-parser',
  parserOptions: {
    parser: '@typescript-eslint/parser', // Specifies the ESLint parser
    ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
    sourceType: 'module', // Allows for the use of imports
    ecmaFeatures: {
      tsx: true, // Allows for the parsing of tsx
      // jsx: true,// Allows for the parsing of JSX
    },
  },
  plugins: ['@typescript-eslint'],
  extends: [
    'plugin:@typescript-eslint/recommended',
    'plugin:vue/vue3-recommended',
    'prettier/@typescript-eslint', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
    'plugin:prettier/recommended', // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
  ],
  rules: {
    // js/ts
    'eol-last': 'error',
    'no-trailing-spaces': 'error',
    'comma-style': ['error', 'last'],
    'comma-dangle': ['error', 'always-multiline'],
    quotes: ['error', 'single', { avoidEscape: true, allowTemplateLiterals: true }],
    camelcase: ['error', { properties: 'never' }],
    semi: ['error', 'never'],
    indent: ['error', 2, { SwitchCase: 1 }],
    'object-curly-spacing': ['error', 'always'],
    'arrow-parens': ['error', 'as-needed'],
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    '@typescript-eslint/no-explicit-any': 'off',
    '@typescript-eslint/member-delimiter-style': [
      'error',
      {
        multiline: {
          delimiter: 'none',
          requireLast: false,
        },
        singleline: {
          delimiter: 'semi',
          requireLast: true,
        },
      },
    ],
    // vue
    'vue/no-v-html': 'off',
    'vue/singleline-html-element-content-newline': 'off',
    'vue/html-self-closing': [
      'error',
      {
        html: {
          void: 'never',
          normal: 'never',
          component: 'always',
        },
      },
    ],
    'vue/max-attributes-per-line': [
      'error',
      {
        singleline: 3,
        multiline: 1,
      },
    ],
    'vue/require-default-prop': 'off',
    'vue/html-closing-bracket-spacing': 'error',
  },
};
  1. 配置 typescript
//vue-shim.d.ts
declare module '*.vue' {
  import { Component, ComponentPublicInstance } from 'vue'
  const _default: Component & {
    new(): ComponentPublicInstance<any>
  }
  export default _default
}
//source.d.ts
import Vue from 'vue'
declare module '*.vue' {
  export default Vue
}

declare module '*.json'
declare module '*.png'
declare module '*.jpg'

仿element-ui的dialog组件

  • css直接使用elemnt的即可,自行去下载主题,并引入
  • dialog组件需要2个组件(全局的mask组件,dialog组件)

//dialog.ts

//dialog抽离的逻辑

// 全局引用

import Dialog from './components/dialog/index'
const app = createApp(App)
app.component(Dialog.name, Dialog)

// mask蒙层组件

//使用方式 v-model.modelValue(原:visible.sync="dialogVisible"上一篇有介绍)

<elDialog
    v-model.modelValue="dialogVisible" 
    title="提示"
    width="30vw"
    @close="handleClose">
    <p>这是一段信息~~~</p>
    <div slot="footer" class="dialog-footer">
      <button @click="handleClose" class="el-button el-button--default">取 消</button>
      <button type="primary" class="el-button el-button--primary" @click="dialogVisible = false">确 定</button>
    </div>
  </elDialog>

总结

至此,使用vue3.0+typescript开发dialog组件后,对vue3.0的开发有了进一步的了解。这个过程中,需要注意安装插件的版本。如果报错了,估计就是版本安装不对。下一篇就开始介绍router了~希望大家喜欢~
代码github地址

查看原文

赞 0 收藏 0 评论 0

阿秋_在路上 发布了文章 · 9月25日

尝鲜vue3.0-了解变化(2)

背景

经过上一篇文章尝鲜vue3.0-从ToDoList开始的介绍,大家可以初步了解一下vue3.0的简单写法。接下来,我描述一下尤大总结的总体变化,以及目前成熟的rfcs(语法变化)。


总体变化

  • Performance(性能)

    1. 重写vdom,提升了静态标记性能
    2. 编译时优化
    3. 更好的初始化性能
    4. 更新更快
    5. ssr速度更快
  • Tree-shaking support(tree shaking支持,文件更小)
    组件不需要一个唯一的根节点
  • Composition API新语法(Vue Composition API
  • Fragment(碎片),Teleport(portal),Suspense(异步组件)(新组件方式)
  • Better Typescript support(更好的支持ts)
  • Custom Renderer API(自定义渲染器)

Active Rfcs

  1. slot语法变化

    • v-slot在单一指令语法中统一了卡槽和槽作用域。
    • v-slot简写为#,可以统一作用域槽和卡槽的使用。

  1. 删除了class component
  2. 删除了 filterinline template,取消v-on的keyCode(@keyup.enter
  3. directive

    //动态转入指令名称
    <div v-bind:[key]="value"></div>
    <div v-on:[event]="handler"></div>
    //
    import { h, resolveComponent, resolveDirective, withDirectives } from 'vue'
    export default {
      render() {
        const comp = resolveComponent('some-global-comp')
        const fooDir = resolveDirective('foo')
        const barDir = resolveDirective('bar')
        // <some-global-comp v-foo="x" v-bar="y" />
        return withDirectives(
          h(comp),
          [fooDir, this.x],
          [barDir, this.y]
        )
      }
    }
  4. 为了运行tree-shaking,将api通过全局的import导出

    //比如
    import {nextTick,h,} from 'vue'
  5. 删除 v-bind.sync,用v-model代替

    • 主要是考虑到v-bind.syncv-model作用相同,使用起来会造成混乱
    • 在vue3里面,将会检测 v-bind.sync然后报warning,并且使用新的迁移助手检测并自动修复100%使用v-bind和.sync的情况

  1. 函数式组件必须为函数

    • 删除 { functional: true }
    • 不再支持 <template functional>
    //与2.X相比
    //只使用props和slot(data和childre就没了)
    //使用的新的api inject代替injections
    //parent被删除,首选使用props 和 injections 
    //listeners 将包括在 attrs 属性中
     import { inject } from 'vue'
     import { demo } from './Demo'
    
     const FunctionalComp = (props, { slots, attrs, emit }) => {
       const demoCom = inject(demo)
       return h('div', `Using demo ${demoCom}`)
     }
    • 必须用defineAsyncComponent来创建异步组件
    import { defineAsyncComponent } from 'vue'
    const AsyncComp = defineAsyncComponent(() => import('./Foo.vue'))
    1. 全局api的改变

改变Vue行为的global API被移到由新的apicreateApp方法创建的应用实例中,他们的只作用于app实例中,不改变Vue行为的gloabl API通过import使用,见第5点

    1. render函数的变化
    • h 使用import从vue中引用,见第五点
    • render函数的传值,将和函数式组件保持一致,见第7点props, { slots, attrs, emit }
    • vNodes变成扁平化的数据结构
    export default {
     render() {
       return h(
         'div',
         //以前
         //{
          // domProps: { innerHTML: '' },
           //on: { click: foo },
           //key: 'foo',
           //class: ['foo', 'bar'],
           //style: { color: 'red' },
           //attrs: { id: 'foo' },
         //},
         // 扁平化的数据结构
         {
           innerHTML: '',
           id: 'app',
           onClick:clickFn,
           key: 'foo',
           class: ['foo', 'bar'],
           style: { color: 'red' }
         },
         [
           h('span', 'world')
         ]
       )
     }
    }
    1. Composition API

      目前变化的api,东西太多讲不完。大概就是上一遍新增的ref,reactive,watchEffect等api

    2. transition变化

      • 当从外部切换组件时,使用<transition>作为组件的根将不再触发转换
      • class变化

        • v-enter-from 替换 v-enter
        • v-leave-from 替换 v-leave
        • v-appear-from 替换 v-appear
    3. 删除$on, $off, $once

      在vue2.0里面,可以用声明式的$emit可触发父组件中的方法,但是也能使用命令式的$on, $off, $once也能实现。算是一种重载,也考虑到也没必要暴露组件实例

    4. 属性的强制行为

      • 删除枚举属性的内部概念,并将这些属性视为普通的非布尔属性。
      • 当值为布尔值时不再移除属性。相反,它被设置为attr="false"。若要删除该属性,请使用null或undefined。

        //vue2.0中原话
        //如果 isButtonDisabled 的值是 null、undefined 或 false,则 disabled attribute 甚至不会被包含在渲染出来的 <button> 元素中
        <button v-bind:disabled="isButtonDisabled">Button</button>

    vue2.0 attribute地址

    1. 在单个文件组件的样式中提供自定义CSS扩展
    • 弃用 >>>/deep/

      //使用这种
      ::v-deep(.bar) {}
    • 新伪类作用域组件块的全局样式::v-global(.foo) {}
    • 当前,从父节点传入的插槽内容同时受到父节点和子节点的样式影响。无法显式的创建以slot内容为目标的样式,或者不影响slot内容的样式,vue3.0中增加::v-slotted()

      ::v-slotted(.foo) {}
    1. Teleport 传送门(具体见上一篇)
    2. 路由相关
      也新增了很多api,但是细节太多,后面再补一篇细节

    最后

    目前的改动简单的列举了一下,vue3.0改动很大,但是更多是整理简化api,增加更多开发者需要的api,内部语法调整,以及性能优化。还是尽量的贴合以前开发习惯,降低升级成本。所以大家不用担心,可以愉快的接受3.0

    喜欢的话,大佬们可以点赞收藏,后续再更新😄
    下一篇尝鲜vue3.0-tyepscript开发组件(3)

    查看原文

    赞 0 收藏 0 评论 0

    阿秋_在路上 发布了文章 · 9月25日

    尝鲜vue3.0-从ToDoList开始(1)

    项目生成

    1. webpack配置的版本
      直接从github clone即可
      github地址
    2. @vue/cli
    npm i -g @vue/cli
    vue create vue-demo-1
    cd vue-demo-1
    vue add vue-next
    npm run serve

    注意

    //package.json可见
    "dependencies": {
      "core-js": "^3.6.5",
      "vue": "^3.0.0-beta.1"
    },
    1. vite

      vite是推荐使用的新工具https://github.com/vitejs/vite

      npm init vite-app vue-demo-2
      cd vue-demo-2
      npm install
      npm run dev

    简单的计数器(介绍部分api)

    HelloWorld.vue代码

    asyncCom.vue

    <script>
    import {onMounted} from 'vue'
    export default {
     name: 'asyncCom',
     async setup(){
       onMounted(()=>{
         console.log('---');
       })
       await sleep()
     }
    }
    function sleep(){
     return new Promise((resolve)=>{
       setTimeout(()=>{
         resolve()
         console.log(1);
       },3000)
     })
    }
    </script>

    ToDoList组件

    • 顶部输入框,enter添加新item
    • list需要单项有checkbox和delete,全选功能,以及选中个数的统计
    • 滚动列表,输入框到一定位置需置顶

      (样式可自己调节)
      toDoList.vue

    最后

    完成上述步骤之后,相信大家对vue3.0有了初步的了解。。。

    生命不息,代码不止。。。

    下一篇介绍vue3.0的变化尝鲜vue3.0-了解变化(2)

    查看原文

    赞 0 收藏 0 评论 0

    阿秋_在路上 关注了标签 · 2018-02-02

    关注 55419

    认证与成就

    • 获得 2 次点赞
    • 获得 2 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 2 枚铜徽章

    擅长技能
    编辑

    开源项目 & 著作
    编辑

    (゚∀゚ )
    暂时没有

    注册于 2017-08-07
    个人主页被 201 人浏览