走进 San CLI(下):实现原理

2023-11-04

作者:胡粤

上期我们讨论了 San CLI 的使用,这期我们再深入一点,来看看 San CLI 的实现原理。

核心模块和核心概念

为了方便理解下文的 San CLI 的整体工作流程(主流程),我们先来看下 San CLI 的核心模块和核心概念。

核心模块
San CLI 的核心模块包括:

  • san-cli:负责 San CLI 的主流程和实现核心功能;
  • san-cli-service:Service 层,负责 Service 流程;
  • san-cli-command-init:实现 san init 命令的 Command 插件;
  • san-cli-plugin-*:Service 插件;
  • san-cli-utils:工具库,在插件中也可以直接使用;
  • san-cli-webpack:webpack build 和 dev-server 的通用逻辑,webpack 自研插件等。

核心概念
核心概念主要有流程和插件,其中,流程又分为主流程和 Service 流程,插件又分为 Command 插件 和 Service 插件:

  • 流程:San CLI 的流程分为两段,主流程和 Service 流程:
  • 主流程: san-cli/index.js 的流程,是整个 San CLI 的工作流程。当我们在命令行输入 San CLI 相关的命令后,比如 san init、 san serve(对应 npm start) 和 san build(对应 npm run build),就会进入主流程,主流程会执行对应命令的 handler。有的命令的 handler 不直接包含处理逻辑,而是引入 san-cli-service,比如 san serve 和 san build,输入这类命令时,就会从主流程进入 Service 流程。
  • Service 流程: san-cli-service/Service.js 的流程,主要处理 Webpack 构建相关的逻辑。
  • 插件:用于扩展 San CLI 的功能:
  • Command 插件:用于主流程,执行 Command 插件定义的命令后主流程就会执行对应的 Command 插件的 hanlder;
  • Service 插件:用于 Service 流程,处理 Webpack 构建相关的逻辑。

整体工作流程

San CLI 的整体工作流程,即主流程,在 san-cli/index.js 中,大致如下:

  1. 检查 node 版本;
  2. 检查 san-cli 版本;
  3. 调用 san-cli/lib/Commander.js 创建命令实例:
    a.添加全局 option;
    b.添加中间件:
    i.设置全局 logLevel;
    ii.设置 NODE_ENV 环境变量;
    iii.给 argv 添加日志等属性和方法。
  4. 加载内置命令: init、 serve、 build、 inspect、 ui 等;
  5. 加载 package.json 和 sanrc.json 里声明的预置命令(自定义命令);

sanrc.json 是配置 San CLI 的文件,和 san.config.js 不同,后者是配置项目的文件,详见 San CLI
官方文档。

  1. 触发命令的 hanlder,开始 San CLI 的正式执行。
    图片
    结合核心模块的主流程如下图所示:
    图片

san init

san init 命令用于初始化项目,用法在上期已有所介绍,这期我们来看该命令是怎么做到初始化一个项目的。

流程
san init 初始化项目的原理,简单来说,就是通过 git 命令远程拉取项目脚手架模板的代码库到本地,或者直接使用本地的项目脚手架模板的代码库,然后使用 vinyl-fs 将拉取到的代码库的文件依次处理,处理完成就得到了一个初始化好的项目。

vinyl-fs 是 gulp 的核心。

san init 主要由四步串行任务组成:

  1. 检查目录和离线包状态:检查项目脚手架模板的本地路径和离线包是否可用;
  2. 下载项目脚手架模板:从 Github 等远程仓库下载项目脚手架模板到模板缓存目录;
  3. 生成项目目录结构:使用 vinyl-fs 把项目脚手架模板从缓存目录遍历处理到开发者指定的项目目录;
  4. 安装项目依赖:询问开发者是否安装 package.json 里的依赖。

对应的流程图如下图所示:
图片
其中,检查目录和离线包状态的流程图如下:
在这里插入图片描述
设计思路
san init 的具体实现在 san-cli-command-init 模块中, san-cli-command-init 模块是一个 Command 插件,其核心是一个 TaskList 类, san init 的执行过程的本质就是:传入上述 4 个任务组成的数组来实例化 TaskList 并调用实例的 run 方法。

我们来看下 TaskList 的内部实现。

当调用 TaskList 实例的 run 方法时,首先会对执行实例化 TaskList 时传入的任务列表的第一个任务进行处理。

因为这些任务本质上都是一个个函数,所以先给第一个任务加上一些方法,比如表示这个任务已完成的 complete 方法,加完方法后,就调用第一个任务函数。

调用第一个任务函数,就意味着第一个任务开始执行了,当第一个任务该做的事情都做完后,最后就会在这个任务函数中调用之前给这个任务函数加上的 complete 方法。

complete 方法就做一件事情,让 TaskList 实例开始处理下一个任务,处理方式和上面说的一样,只是简单地重复。

最后, TaskList 实例发现没有下一个任务了,就收工了。

TaskList 源码简化版
我们用简化版的源码来看下 TaskList 的使用和实现。

TaskList 的使用:

// 任务列表,4 个任务函数分别对应 san init 的 4 个串行任务
const taskList = [checkStatus, download, generator, installDep];
// 传入任务列表来实例化 TaskList
const tasks = new TaskList(taskList);
// 按任务列表的顺序依次执行任务
tasks.run();

以 checkStatus 为例,看下任务函数的实现:

function checkStatus(task) {
    // 检查目录和离线包状态
    // ……
    // 检查完了,告诉 tasks 这个任务完成了
    task.complete();
}

TaskList 的实现:

class TaskList {
    constructor(taskList) {
        this._taskList = taskList;
        // 当前任务的索引
        this._index = 0;
    }
    run() {
        const currentTask = this._taskList[this._index];
        // 给当前任务的函数加上 complete 方法
        currentTask.complete = () => {
            // 当前任务完成,执行下一个任务
            this.next();
        };
        // 真正开始执行当前任务
        // 把任务函数作为入参是用于在任务函数里调用 complete 方法,具体可见上面的 checkStatus 任务函数的实现
        currentTask(currentTask);
    }
    next() {
        this._index++;
        if (this._index >= this._taskList.length) {
            // 所有任务都完成了,收工
            return;
        } else {
            // 还有任务没完成,继续执行下一个任务
            this.run();
        }
    }
}

我们可以看出, san init 的设计模式还是比较优秀的,举个栗子,如果我们想给 san init 添加多一个任务,那只需要关注该任务本身的实现,实现好后放入实例化 TaskList 时传入的任务列表中,就可以了,可扩展性非常好。

插件机制

San CLI 的插件分为 Command 插件和 Service 插件,在上期我们以实际例子讨论了具体怎么开发一个 Command 插件或 Service 插件了,这期我们就来看看 San CLI 的插件机制。

Command 插件
Command 插件相对 Service 插件来说,机制比较简单。

Command 插件实际是 yargs 的插件系统的扩展,yargs 是一个 npm 包,用它我们可以定义我们自己的命令行命令。

回顾下我们在上期创建的 Command 插件:

// san-command-hello.js
exports.command = 'hello';
exports.builder = {
    name: {
        type: 'string'
    }
};
exports.desc = '热情地向给定对象打招呼';
exports.handler = argv => {
    console.log(`${argv.name},你好呀!`);
};

之所以要这么写,是因为这是 yargs 对定义一个命令的要求。定义好命令后,就在项目的 package.json 里声明这个命令。

当我们执行任何一个 san 的命令时 —— 注意,是任何一个 —— 在真正执行这个命令之前,San CLI 会先去读取 package.json 里声明的命令,然后找到命令的定义并传入 yargs,此时,yargs 就知道了都有些什么命令,在此之后,San CLI 才把我们执行的命令的名字和参数传如 yargs,yargs 拿到命令的名字和参数后,就回去执行对应命令的 hanlder。

Command 插件的机制就是这样。

另外还值得注意的是,在 san-cli/lib/commander.js 里定义了一个名为 Command 的类,这个类对 yargs 插件做了一些定制,比如通过中间件机制添加了常用的方法和属性到 argv 对象中,方便下游 handler 直接使用。

Service 插件

San CLI 的 Service 插件机制借鉴了 Vue CLI 的 Service 插件机制,但有一些不同之处:

  1. Vue CLI 注册一个新命令是通过 Service 插件来完成的,具体是使用 Service 插件的
    registerCommandAPI 方法实现;而 San CLI 把注册一个新命令的逻辑从 Service
    插件里分离了出来,成为了一个独立的部分,也就是前面介绍过的 Command 插件。

  2. Vue CLI 的一个命令对应一个或多个 Service 插件,也就是说,一个命令的实现由一个或多个 Service 插件来完成;而
    San CLI 的一个命令对应零个或所有 Service 插件(引入的),一个命令对应零个的情况是这个命令是一个纯的 Command
    插件,一个命令对应所有 Service 插件的情况是这个命令在它对应的 Command 插件的逻辑里触发了 Service 流程,而
    Service 流程会依次注册并执行所有的 Service 插件。

下面我们会以 san serve 命令为例,分别看下 Service 流程、Service 插件的设计思路和 Service 类的简化版源码

Service 流程

Service 流程,即 Service 的整个工作流程:

  1. San CLI 在主流程解析输入命令行的 san serve 命令,进入 san-cli/commands/serve 的
    handler;
  2. san serve 命令的 handler 主要是实例化 Service,实例化会将配置项和 Service 插件进行处理;
  3. 执行 service.run(callback),进入 Service 流程,Service 流程的实现主要在 service.run 中:
    a.loadEnv:加载 env 文件;
    b.loadProjectOptions:加载 san.config.js;
    c.init:service 启动:
    i.初始化插件,即依次执行插件;
    ii.依次执行 webpackChain 回调栈;
    iii.依次执行 webpackConfig 回调栈;
  4. 执行 callback。

对应的流程图如下:
在这里插入图片描述

我们自定义的 Service 插件的具体执行时机是在 3-1-1 “初始化插件,即依次执行插件” 这一步,对应上图中的 “初始化插件(插件.apply)” 这一步。

上图中的 “初始化 plugin 变量并加载传入的 plugin” 这一步和 “加载 config 中 plugins 里设置的插件” 这一步,其实都是在加载 Service 插件,只不过前者是在加载内置插件和 sanrc.json 里预设的插件,而后者主要是在加载 san.config.json 里的插件。

加载 Service 插件的流程图如下:
图片

设计思路
输入 san serve 命令,触发对应的 handler。 handler 主要就做两件事情:一是实例化 Service,二是调用 Service 实例的 run 方法。

实例化 Service 时,会加载内置 Service 插件和 sanrc.json 里预设的 Service 插件。如果我们自定义的 Service 插件预设在了 sanrc.json 里,比如上期的 san-cli-plugin-get-entry,这个时候就会被加载了。

实例化完 Service,就调用 Service 实例的 run 方法。

调用 run 方法时,首先会加载 san.config.js 里的 Service 插件,当然也包括我们放在 san.config.js 里的自定义 Service 插件;然后,该加载的 Service 插件都加载完了,这时就准备依次执行它们了。

在执行每个 Service 插件之前,会先实例化 PluginAPI。 PluginAPI 实例给 Service 插件提供了用于处理 Webpack 构建相关逻辑的方法,比如 configWebpack,通过这个方法我们可以在 Service 插件里获取和修改 Webpack 配置,比如在上期我们写的 Service 插件示例里,就用这个方法获取了网站的入口文件名。

最后,把 PluginAPI 实例作为入参来调用 Service 插件定义的 apply 函数,就正式开始了 Service 插件的执行。

Service 源码简化版
Service 的使用:

// san serve 命令的 handler
exports.handler = argv => {
    // 实例化 Service
    // 这里传入的 argv.plugins 是内置 Service 插件和 sanrc.json 里预设的 Service 插件
    // 之所以能通过 argvs 获取到这些插件,是因为正如在上文的“Command 插件”一节里所说,我们对 yargs 插件做了一些定制
    const service = new Service(argv.plugins);
    // 调用 Service 实例的 run 方法
    service.run();
};

Service 的实现:

class Service {
    constructor(plugins) {
        // 加载内置 Service 插件和 `sanrc.json` 里预设的 Service 插件
        // 这是第一批 Service 插件
        this.plugins = this.loadPlugin(plugins);
    }
    run() {
        // 获取 san.config.js 里的配置
        const projectOptions = this.loadProjectOptions();
        // 加载 san.config.js 里配置的 Service 插件
        // 这是第二批 Service 插件
        const morePlugins = this.loadPlugin(projectOptions.plugins);
        // 合并两批 Service 插件
        this.plugins = [...this.plugins, ...morePlugins];
        // 依次处理 Service 插件
        this.plugins.forEach(plugin => {
            // 实例化 PluginAPI,得到用于处理 Webpack 构建相关逻辑的方法
            const pluginApi = new PluginAPI();
            // 把用于处理 Webpack 构建相关逻辑的方法传入 Service 插件定义的 apply 函数,正式开始了Service 插件的执行
            plugin.apply(pluginApi);
        });
    }
    loadPlugin() {
        // 主要是统一各路 Service 插件的形式,方便后续调用
        // 具体做了些什么可见前面的加载 Service 插件的流程图
        // ……
    }
    loadProjectOptions() {
        // 实现获取 san.config.js 里的配置
        // ……
    }
}

PluginAPI 的实现:

class PluginAPI {
    configWebpack(fn) {
        // 实现 configWebpack
        // ……
    }
}

最后

感谢你阅读到了这里,以上便是《走进 San CLI(下):实现原理》的全部内容。

如果你都看懂了,请收下我的膝盖:

图片

作为 San 生态系列文章的第二弹的《走进 San CLI》,也告一段落了,敬请期待下周同样精彩的 San 生态系列文章之 San CLI UI !

新的一年San-CLI还会有持续的开发和优化,比如eject功能、CLI和Service要不要分离,想了解后续的更新,可以关注San CLI 的 GitHub,欢迎 star、欢迎 issues、欢迎 pr。地址:https://github.com/ecomfe/san-cli

作者:胡粤

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

走进 San CLI(下):实现原理 的相关文章

  • 在CLI中打印表格----gotable使用介绍

    目录 介绍 获取gotable 在github中获取 下载源码 git clone go mod API 创建table 从结构体中创建空table 获取版本信息 获取版本列表 打印表格 给表格添加行 给表格添加多个行 给表格添加列 介绍
  • signature=f9281a43abd6807852c6bbdc0be5748c,vue-cli-3-ssr/yarn.lock at master · x007xyz/vue-cli-3-ssr...

    cross spawn 64 5 0 1 cross spawn 64 5 1 0 version 34 5 1 0 34 resolved 34 https registry npmjs org cross spawn cross spa
  • vue-cli 插件开发补充

    官网地址 xff1a https cli vuejs org zh api地址 xff1a https cli vuejs org dev guide plugin api html Plugin API api version 64 vu
  • 【Github CLI】Take GitHub to the command line

    目录儿 一 Git Github GitLab二 Github CLI gh2 1 gh简介2 2 gh的使用2 21 Github身份验证 xff08 必选 xff09 2 22 常用命令 xff08 1 xff09 在Github仓库中
  • ovsdb <10> Ops-cli结合ovsdb部分流程及接口分析

    4 6Ops cli结合ovsdb部分流程及接口分析 Ops中对于ovsdb的应用主要是基于上述的ovsdb的简单用法上的一个扩展和函数封装 xff0c 现在就其与之前不同的地方做补充说明并就各个函数的使用做相应的说明 根据前面的ovsdb
  • vue报错: npm run serve报错 ‘vue-cli-service‘ 不是内部或外部命令,也不是可运行的程序

    原因 xff1a 该目录下node modules gt bin gt 没有vue cli service文件 这个时候运行npm run serve 或 npm run dev就会报错 加我weixin xff0c 可帮忙解决 做法一 x
  • 01-搭建Vue脚手架(vue-cli)

    一 那么我们就从最简单的环境搭建开始 xff1a 安装node js xff0c 从node js官网下载并安装node xff0c 安装过程很简单 xff0c 一路 下一步 就可以了 xff08 傻瓜式安装 xff09 安装完成之后 xf
  • AWS CLI version 2 在 Windows 下的安装

    首先你需要到下面的链接地址中下载需要的应用 xff1a https awscli amazonaws com AWSCLIV2 msi 双击运行 双击运行下载的可以运行的文件 下一步继续 下一步继续安装进程 许可协议 你需要同意许可协议后继
  • BetaFlight模块设计之三十:Cli模块分析

    BetaFlight模块设计之三十 xff1a Cli模块分析 Cli模块Cli接口Cli框架Cli命令结构主要函数分析cliProcess函数processCharacterInteractive函数processCharacter函数
  • pomelo源码解析--新建项目(cli工具: pomelo)

    pomelo怎么新建项目 官方文档 1 安装pomelo 2 新建项目HelloWorld 我简单整理了下创建新项目关键步骤 xff1a 安装pomelo 方式一 xff1a npm install pomelo g 方式二 xff1a g
  • webpack One CLI for webpack must be installed. These are recommended choices, delivered as separate

    场景 安装webpack的时候 遇到的情况 直接按照提示安装 提示Module webpack cli 不存在 One CLI for webpack must be installed These are recommended choi
  • sh: 1: vue-cli-service: Permission denied

    看报错日志 xff0c 权限被拒绝 进入node modules bin 34 ll 34 查看一下会发现该文件 vue cli service 34 并没有可执行权限 chmod R 755
  • vue-cli-service Axios 持续返还401

    后端 xff1a 在登录超时或没有登录的情况 xff0c 所有请求都会拒绝并且返还HTTP状态码401 前端 xff1a 前端将认证的Token放到loadStorage中 xff0c 在认证失效时候清空 在接到HTTP状态码为401时 x
  • 多任务视频推荐方案,百度工程师实战经验分享

    推荐系统的应用场景非常广泛 比如非常火爆的短视频推荐 电商平台商品推荐 搜索推荐等 但是你知道吗 短视频APP在向你展示一个你感兴趣的视频之前 通常既要预测你对这个视频是否感兴趣 又要预测你会看多久 点赞还是不点赞 会不会偷偷收藏起来下次接
  • 百度APP视频播放中的解码优化

    背景 在全民视频的时代 百度APP中视频播放是十分重要的业务 随着 5G 的到来 视频播放已经不满足以前的标清 高清 超清乃至于 4K 已经是旧时王谢堂前燕飞入寻常百姓家 越来越清晰的视频源 越来越复杂的视频编码 对 APP 的视频解码能力
  • 使用cobra创建cli命令行工具

    什么是cobra Cobra既是用于创建强大的现代CLI应用程序的库 也是用于生成应用程序和命令文件的程序 Cobra是一个库 提供了一个简单的界面来创建类似于git go工具的强大的现代CLI界面 Cobra也是一个应用程序 它将生成您的
  • 走进 San CLI(上):使用介绍

    本文是 San CLI 的使用和原理的第一篇 主要介绍 San CLI 的初衷和使用 下一篇介绍具体的实现原理 什么是 CLI CLI 是命令行界面 command line interface 的英文缩写 命令行界面是在图形用户界面得到普
  • 培训第二弹 全国大学生智能汽车竞赛百度竞速组预告

    3月12日本周六晚7点 百度飞桨B站直播间 第十七届全国大学生智能汽车竞赛完全模型组竞速赛第二次线上培训正式开讲 卓老师年前来百度科技园 检查作业 培训内容和学习资料筹备会 不放过每个细节 经过长期精心筹备 在3月12日晚7点飞桨B站直播间
  • 走进 San CLI(下):实现原理

    作者 胡粤 上期我们讨论了 San CLI 的使用 这期我们再深入一点 来看看 San CLI 的实现原理 核心模块和核心概念 为了方便理解下文的 San CLI 的整体工作流程 主流程 我们先来看下 San CLI 的核心模块和核心概念
  • 18M 超轻量系统开源

    图像识别作为深度学习算法的主流实践应用方向 早已在生活的各个领域发挥作用 如安全检查和身份核验时的人脸识别 无人货架和智能零售柜中的商品识别 这些任务背后的关键技术都在于此 图1 PP ShiTu应用于商品识别效果示意 开发者应用展示 然而

随机推荐

  • 基于接口设计原则-java

    7种设计坏味道 1 僵化性 很难对系统进行改动 因为每个改动都会迫使许多对系统其他部分的其它改动 2 脆弱性 对系统的改动会导致系统中和改动的地方在概念上无关的许多地方出现问题 3 牢固性 很难解开系统的纠结 使之成为一些可在其他系统中重用
  • PyTorch中squeeze()和unsqueeze()函数理解

    squeeze arg 表示若第arg维的维度值为1 则去掉该维度 否则tensor不变 即若tensor shape arg 1 则去掉该维度 例如 一个维度为2x1x2x1x2的tensor 不用去想它长什么样儿 squeeze 0 就
  • 小程序:TypeError: Cannot read property ‘restore‘ of undefined

    去看了一些文字说肯能是data里面定义了 restore 而且还是引用类型 数据没有出来就渲染了也就导致了报错undefind 但是自己去看了一些data里面没有定义 restore 也是看了许久的文章才发现一篇文章说是 小程序的版本要降低
  • 笔试

    文章目录 前言 39 跨时钟域问题 1 从亚稳态入手 2 跨时钟域传输的几种情况 3 单bit信号的跨时钟域传输 3 1单bit信号从慢时钟域到快时钟域传输 3 2单bit信号从快时钟域到慢时钟域传输 脉冲同步器 窄脉冲捕捉电路 下期预告
  • 缓动 css,[CSS3] CSS缓动曲线整理

    常用效果 easeInSine cubic bezier 0 47 0 0 745 0 715 easeOutSine cubic bezier 0 39 0 575 0 565 1 easeInOutSine cubic bezier 0
  • SD卡SPI模式 读写block

    声明 第一次写教程 如若有错误 请指出更正 看了很多网上的教程 还是觉得很多教程中 写多个块的时候有些问题 因此经过3天的奋斗 写出自己的教程 本教程中 没有挂载文件系统 单纯读写Block 会破坏分区和数据 下节再 装上文件系统Fatfs
  • Html5监听返回事件

    常使用的场景 移动前端 1 安卓手机物理返回键 2 苹果手机在企业微信打开浏览器的返回按钮 移动前端开发语言是 vue 路由跳转 vue router hash模式 先介绍一下html5中history有哪些属性和API 我们用到了其中2个
  • Pcl粗配准矩阵不唯一

    经过一段时间的调试 发现转换矩阵不唯一 因此要将两个点云的数值减去一个值 使之最小 受到矩阵相乘相加受到的影响最小
  • 继承:父类和子类的关系

    继承 父类和子类的关系 一 父类引用指向子类对象时 1 若子类覆盖了某方法 则父类引用调用子类重新定义的新方法 2 若子类未覆盖某方法 则父类引用调用父类本身的旧方法 3 若子类覆盖了某属性 但父类引用仍调用父类本身的旧属性 4 若子类未覆
  • Linux命令后台运行

    Linux后台运行命令有两种方式 cmd 后台运行 关掉终端会停止运行 nohup cmd 后台运行 关掉终端不会停止运行 方式一 cmd cmd 实现让命令在后台运行 但不要将有用户交互的命令放到后台 这样命令会在后台等待用户输入 后台运
  • [leetcode160]

    Definition for singly linked list class ListNode object def init self x self val x self next None class Solution object
  • 用ChatGPT后被海外名校录取,泰库啦!!

    世界之大无奇不有 有人竟然因为使用ChatGPT后被海外大学录取 ChatGPT真的那么强大吗 竟然有这样子的能力 国内一些朋友因为各种问题没有办法使用ChatGPT 文章后面会给大家分享国内ChatGPT免注册免费使用的方法教程 今天一看
  • web安全的常用靶场

    文章目录 1 sqlmap 2 Test Lab 方便测试 列出以下WEB的靶场 仅供参考 1 sqlmap 0 作者出的漏洞测试环境 sqlmap 1 owasp 2 owaspbwa 3 xvwa 4 webgoad 5 DVWA 6
  • ananconda添加镜像

    先执行 conda config set show channel urls yes 生成该文件之后再修改 在用户目录下的 修改 condarc 文件 channels defaults show channel urls true cha
  • 联想小新air14安装ubuntu16.04

    首先正常安装 会遇到显卡问题 进入ubuntu高级模式 recovery mode resume 然后按照这篇教程整 https blog csdn net Guangli R article details 86636923 utm so
  • PCB设计 接地 铺铜的技巧

    PCB设计 接地 铺铜的技巧 1 PCB设计接地敷铜的技巧 pcb 电工之家 2 覆铜步骤及设计规则 百度文库 3 产品可靠性1 多层电路板应不应该在顶层和底层铺铜 Steven Aileen的博客 CSDN博客 4层板顶底层还用铺铜吗
  • 淘宝、支付宝菜鸟小程序取件码找不到的解决方法

    淘宝 支付宝菜鸟小程序身份码找不到的解决方法 今天拿快递的时候在淘宝里面找身份码 死活找不到 明明之前可以找到的 最后还是下载了菜鸟裹裹app才能取件 上网搜索了一下 发现有的地方很早就把小程序里面的身份码给阉割了 强行让用户下载app实属
  • Evaluate Video Quality

    How to evaluate video PSNR and SSIM PSNR is easy to calculate http blog csdn net c602273091 article details 49861817 SSI
  • 跟着angularjs2官方文档学习(五)

  • 走进 San CLI(下):实现原理

    作者 胡粤 上期我们讨论了 San CLI 的使用 这期我们再深入一点 来看看 San CLI 的实现原理 核心模块和核心概念 为了方便理解下文的 San CLI 的整体工作流程 主流程 我们先来看下 San CLI 的核心模块和核心概念