zoukankan      html  css  js  c++  java
  • wepy原理研究

    像VUE一样写微信小程序-深入研究wepy框架 https://zhuanlan.zhihu.com/p/28700207

    wepy原理研究

    虽然wepy提升了小程序开发体验,但毕竟最终要运行在小程序环境中,归根结底wepy还是需要编译成小程序 需要的格式,因此wepy的核心在于代码解析与编译。
    wepy项目文件主要有两个: wepy-cli:用于把.wpy文件提取分析并编译成小程序所要求的wxml、wxss、js、json格式 wepy:编译后js文件中的js框架

    wepy编译过程


    拆解过程核心代码

    //wepy自定义属性替换成小程序标准属性过程
    return content.replace(/<([w-]+)s*[sS]*?(/|</[w-]+)>/ig, (tag, tagName) => {
       tagName = tagName.toLowerCase();
    return tag.replace(/s+:([w-_]*)([.w]*)s*=/ig, (attr, name, type) => { // replace :param.sync => v-bind:param.sync
    if (type === '.once' || type === '.sync') {
    }
    else
               type = '.once';
    return ` v-bind:${name}${type}=`;
    }).replace(/s+@([w-_]*)([.w]*)s*=/ig, (attr, name, type) => { // replace @change => v-on:change
    const prefix = type !== '.user' ? (type === '.stop' ? 'catch' : 'bind') : 'v-on:';
    return ` ${prefix}${name}=`;
    });
    });
    
    ...
    //按xml格式解析wepy文件
    xml = this.createParser().parseFromString(content);
    const moduleId = util.genId(filepath);
    //提取后的格式
    let rst = {
       moduleId: moduleId,
       style: [],
    template: {
           code: '',
           src: '',
           type: ''
    },
       script: {
           code: '',
           src: '',
           type: ''
    }
    };
    //循环拆解提取过程
    [].slice.call(xml.childNodes || []).forEach((child) => {
    const nodeName = child.nodeName;
    if (nodeName === 'style' || nodeName === 'template' || nodeName === 'script') {
    let rstTypeObj;
    
    if (nodeName === 'style') {
               rstTypeObj = {code: ''};
               rst[nodeName].push(rstTypeObj);
    } else {
               rstTypeObj = rst[nodeName];
    }
    
           rstTypeObj.src = child.getAttribute('src');
           rstTypeObj.type = child.getAttribute('lang') || child.getAttribute('type');
    if (nodeName === 'style') {
    // 针对于 style 增加是否包含 scoped 属性
               rstTypeObj.scoped = child.getAttribute('scoped') ? true : false;
    }
    
    if (rstTypeObj.src) {
               rstTypeObj.src = path.resolve(opath.dir, rstTypeObj.src);
    }
    
    if (rstTypeObj.src && util.isFile(rstTypeObj.src)) {
    const fileCode = util.readFile(rstTypeObj.src, 'utf-8');
    if (fileCode === null) {
    throw '打开文件失败: ' + rstTypeObj.src;
    } else {
                   rstTypeObj.code += fileCode;
    }
    } else {
    [].slice.call(child.childNodes || []).forEach((c) => {
                   rstTypeObj.code += util.decode(c.toString());
    });
    }
    
    if (!rstTypeObj.src)
               rstTypeObj.src = path.join(opath.dir, opath.name + opath.ext);
    }
    });
    ...
    // 拆解提取wxml过程
    (() => {
    if (rst.template.type !== 'wxml' && rst.template.type !== 'xml') {
    let compiler = loader.loadCompiler(rst.template.type);
    if (compiler && compiler.sync) {
    if (rst.template.type === 'pug') { // fix indent for pug, https://github.com/wepyjs/wepy/issues/211
    let indent = util.getIndent(rst.template.code);
    if (indent.firstLineIndent) {
                       rst.template.code = util.fixIndent(rst.template.code, indent.firstLineIndent * -1, indent.char);
    }
    }
    //调用wxml解析模块
    let compilerConfig = config.compilers[rst.template.type];
    
    // xmldom replaceNode have some issues when parsing pug minify html, so if it's not set, then default to un-minify html.
    if (compilerConfig.pretty === undefined) {
                   compilerConfig.pretty = true;
    }
               rst.template.code = compiler.sync(rst.template.code, config.compilers[rst.template.type] || {});
               rst.template.type = 'wxml';
    }
    }
    if (rst.template.code)
           rst.template.node = this.createParser().parseFromString(util.attrReplace(rst.template.code));
    })();
    
    // 提取import资源文件过程
    (() => {
    let coms = {};
       rst.script.code.replace(/imports*([w-\_]*)s*froms*['"]([w-\_./]*)['"]/ig, (match, com, path) => {
           coms[com] = path;
    });
    
    let match = rst.script.code.match(/[s
    ]componentss*=[s
    ]*/);
       match = match ? match[0] : undefined;
    let components = match ? this.grabConfigFromScript(rst.script.code, rst.script.code.indexOf(match) + match.length) : false;
    let vars = Object.keys(coms).map((com, i) => `var ${com} = "${coms[com]}";`).join('
    ');
    try {
    if (components) {
               rst.template.components = new Function(`${vars}rnreturn ${components}`)();
    } else {
               rst.template.components = {};
    }
    } catch (e) {
           util.output('错误', path.join(opath.dir, opath.base));
           util.error(`解析components出错,报错信息:${e}rn${vars}rnreturn ${components}`);
    }
    })();
    ...
    
    wepy中有专门的script、style、template、config解析模块 以template模块举例:
    //compile-template.js
    ...
    //将拆解处理好的wxml结构写入文件
    getTemplate (content) {
       content = `<template>${content}</template>`;
    let doc = new DOMImplementation().createDocument();
    let node = new DOMParser().parseFromString(content);
    let template = [].slice.call(node.childNodes || []).filter((n) => n.nodeName === 'template');
    
    [].slice.call(template[0].childNodes || []).forEach((n) => {
           doc.appendChild(n);
    });
    ...
    return doc;
    },
    //处理成微信小程序所需的wxml格式
    compileXML (node, template, prefix, childNodes, comAppendAttribute = {}, propsMapping = {}) {
    //处理slot
    this.updateSlot(node, childNodes);
    //处理数据绑定bind方法
    this.updateBind(node, prefix, {}, propsMapping);
    //处理className
    if (node && node.documentElement) {
    Object.keys(comAppendAttribute).forEach((key) => {
    if (key === 'class') {
    let classNames = node.documentElement.getAttribute('class').split(' ').concat(comAppendAttribute[key].split(' ')).join(' ');
                   node.documentElement.setAttribute('class', classNames);
    } else {
                   node.documentElement.setAttribute(key, comAppendAttribute[key]);
    }
    });
    }
    //处理repeat标签
    let repeats = util.elemToArray(node.getElementsByTagName('repeat'));
    ...
    
    //处理组件
    let componentElements = util.elemToArray(node.getElementsByTagName('component'));
    ...
    return node;
    },
    
    //template文件编译模块
    compile (wpy){
    ...
    //将编译好的内容写入到文件
    let plg = new loader.PluginHelper(config.plugins, {
           type: 'wxml',
           code: util.decode(node.toString()),
           file: target,
           output (p) {
               util.output(p.action, p.file);
    },
    done (rst) {
    //写入操作
               util.output('写入', rst.file);
               rst.code = self.replaceBooleanAttr(rst.code);
               util.writeFile(target, rst.code);
    }
    });
    }
    

    编译前后文件对比

    wepy编译前的文件:
    <scroll-view scroll-y="true" class="list-page" scroll-top="{{scrollTop}}" bindscrolltolower="loadMore">
    <!-- 商品列表组件 -->
    <view class="goods-list">
    <GoodsList :goodsList.sync="goodsList" :clickItemHandler="clickHandler" :redirect="redirect" :pageUrl="pageUrl"></GoodsList>
    </view>
    </scroll-view>
    
    wepy编译后的文件:
    <scroll-view scroll-y="true" class="list-page" scroll-top="{{scrollTop}}" bindscrolltolower="loadMore">
    <view class="goods-list">
    <view wx:for="{{$GoodsList$goodsList}}" wx:for-item="item" wx:for-index="index" wx:key="{{item.infoId}}" bindtap="$GoodsList$clickHandler" data-index="{{index}}" class="item-list-container{{index%2==0 ? ' left-item' : ''}}">
    <view class="item-img-list"><image src="{{item.pic}}" class="item-img" mode="aspectFill"/></view>
    <view class="item-desc">
    <view class="item-list-title">
    <text class="item-title">{{item.title}}</text>
    </view>
    <view class="item-list-price">
    <view wx:if="{{item.price && item.price>0}}" class="item-nowPrice"><i>¥</i>{{item.price}}</view>
    <view wx:if="{{item.originalPrice && item.originalPrice>0}}" class="item-oriPrice">¥{{item.originalPrice}}</view>
    </view>
    <view class="item-list-local"><view>{{item.cityName}}{{item.cityName&&item.businessName?' | ':''}}{{item.businessName}}    </view>
    </view>
    </view>
    <form class="form" bindsubmit="$GoodsList$sendFromId" report-submit="true" data-index="{{index}}">
    <button class="submit-button" form-type="submit"/>
    </form>
    </view>
    </view>
    </view>
    </scroll-view>
    
    可以看到wepy将页面中所有引入的组件都直接写入页面当中,并且按照微信小程序的格式来输出 当然也从一个侧面看出,使用wepy框架后,代码风格要比原生的更加简洁优雅

    以上是wepy实现原理的简要分析,有兴趣的朋友可以去阅读源码()。 综合来讲,wepy的核心在于编译环节,能够将优雅简洁的类似VUE风格的代码,编译成微信小程序所需要的繁杂代码。

    wepy作为一款优秀的微信小程序框架,可以帮我们大幅提高开发效率,在为数不多的小程序框架中一枝独秀,希望有更多的团队选择wepy。

    PS:wepy也在实现小程序和VUE代码同构,但目前还处在开发阶段,如果未来能实现一次开发,同时产出小程序和M页,将是一件非常爽的事情。
     
     
  • 相关阅读:
    路面修整
    路由器安置
    高维网络
    SRETAN
    对象、数组 深度复制,支持对象嵌套数组、数组嵌套对象
    仿 window对象 confirm方法
    仿 window对象 alert 方法
    饼状图
    柱状图
    树状图
  • 原文地址:https://www.cnblogs.com/rsapaper/p/9528500.html
Copyright © 2011-2022 走看看