>위챗 애플릿 >미니 프로그램 개발 >실용장---WeChat 미니 프로그램 엔지니어링 탐색 웹팩

실용장---WeChat 미니 프로그램 엔지니어링 탐색 웹팩

coldplay.xixi
coldplay.xixi앞으로
2020-09-19 10:07:243244검색

실용장---WeChat 미니 프로그램 엔지니어링 탐색 웹팩

관련 학습 권장 사항: WeChat 미니 프로그램 튜토리얼

Foreword

WeChat 미니 프로그램은 매우 빠른 속도로 확산되고 편리한 사용으로 인해 많은 사용자를 끌어 모으고 있습니다. 시장 수요가 급격히 증가함에 따라 모든 인터넷 회사는 이점을 활용하고 싶어하므로 소규모 프로그램 개발 기술을 습득하는 것은 의심할 여지 없이 프론트 엔드 개발자에게 없어서는 안될 기술입니다. 그러나 소규모 프로그램 개발에는 항상 개발자들로부터 비판을 받아온 몇 가지 불편함이 있습니다. 주요 증상은 다음과 같습니다.

  • 초기 단계에서 편리한 npm 패키지 관리 메커니즘이 부족합니다. 현 단계에서 사용 중인데 조작이 정말 불편함)
  • 미리 설치된 패키지를 사용할 수 없습니다. 컴파일된 언어 처리 스타일
  • 스크립트 명령을 통해 다른 개발 환경으로 전환할 수 없으며, 해당 환경에 필요한 구성을 수동으로 수정해야 함(일반 프로젝트에는 최소한 개발 및 생산 환경이 있어야 함)
  • 사양 확인 도구를 프로젝트에 통합할 수 없습니다(예: EsLint, StyleLint 사용)

많은 질문을 받은 후, 현대 엔지니어링 기술을 프로젝트에 결합하는 방법에 대해 생각하기 시작했습니다. 작은 프로그램. 초기에 커뮤니티에서 정보를 찾아보니 많은 선배님들이 gulp를 기반으로 연습을 많이 하신 것 같았는데, 작은 프로그램 등 여러 페이지의 애플리케이션에는 gulp의 스트리밍 작업 방식이 더 편리한 것 같습니다. 실제 실습 후에는 gulp를 활용한 솔루션이 만족스럽지 않아 webpack에 대한 실무 탐색으로 전환했습니다. 비록 gulp보다 구현하기가 더 어렵지만 webpack을 엔지니어링 지원으로 선택하는 것이 향후 개발에 확실히 큰 영향을 미칠 것이라고 생각합니다.

Practice

사전 컴파일, 사양 및 기타 더 복잡한 문제를 고려하지 맙시다. 우리의 첫 번째 목표는 webpack을 사용하여 소스 코드 폴더의 파일을 대상 폴더에 출력하는 방법 다음으로 이 프로젝트를 단계별로 생성합니다.

/* 创建项目 */$ mkdir wxmp-base$ cd ./wxmp-base/* 创建package.json */$ npm init/* 安装依赖包 */$ npm install webpack webpack-cli --dev复制代码

종속성을 설치한 후 이 프로젝트에 대한 프로젝트를 생성합니다. 기본 디렉터리 구조는 다음과 같습니다. 그림에 표시된 대로:

실용장---WeChat 미니 프로그램 엔지니어링 탐색 웹팩

위 그림은 app 전역 구성 파일과 페이지만 포함된 가장 간단한 작은 프로그램을 보여줍니다. 다음으로 글로벌 상황이나 페이지에 관계없이 파일 형식을 처리해야 하는 js 형식의 파일과 직접 복사할 수 있는 wxml, 로 구분합니다. 추가 처리 없이 wxss, json 파일. 이런 생각으로 webpack 실행을 위한 설정 파일을 작성하기 시작했고, 프로젝트 루트 디렉터리에 webpack.config.js 파일을 저장할 빌드 디렉터리를 생성했습니다. app全局配置文件和一个home页面。接下来我们不管全局或是页面,我们以文件类型划分为需要待加工的js类型文件和不需要再加工可以直接拷贝的wxmlwxssjson文件。以这样的思路我们开始编写供webpack执行的配置文件,在项目根目录下创建一个build目录存放webpack.config.js文件。

$ mkdir build$ cd ./build$ touch webpack.config.js复制代码
/** webpack.config.js */const path = require('path');const CopyPlugin = require('copy-webpack-plugin');const ABSOLUTE_PATH = process.cwd();module.exports = {  context: path.resolve(ABSOLUTE_PATH, 'src'),  entry: {    app: './app.js',    'pages/home/index': './pages/home/index.js'
  },  output: {    filename: '[name].js',    path: path.resolve(ABSOLUTE_PATH, 'dist')
  },  module: {    rules: [
      {        test: /\.js$/,        exclude: /node_modules/,        use: {          loader: 'babel-loader',          options: {            presets: ['@babel/preset-env'],            plugins: ['@babel/plugin-transform-runtime'],
          },
        },
      }
    ]
  },  plugins: [    new CopyPlugin([
      {        from: '**/*.wxml',        toType: 'dir',
      },
      {        from: '**/*.wxss',        toType: 'dir',
      },
      {        from: '**/*.json',        toType: 'dir',
      }
    ])
  ]
};复制代码

在编写完上述代码之后,为大家解释一下上述的代码究竟会做些什么:

  1. 入口entry对象中我写了两个属性,意在将app.jshome/index.js作为webpack的构建入口,它会以这个文件为起始点创建各自的依赖关系,这样当我们在入口文件中引入其他文件时,被引入的文件也能被webpack所处理。
  2. module中我使用了babel-loaderjs文件进行ES6转换为ES5的处理,并且加入了对新语法的处理,这样我们就解决了在原生小程序开发中总是要反复引入regenerator-runtime的问题。(这一步我们需要安装@babel/core@babel/preset-env@babel/plugin-transform-runtime@babel/runtimebabel-loader这几个依赖包)
  3. 使用copy-webpack-plugin来处理不需要再加工的文件,这个插件可以直接将文件复制到目标目录当中。

我们了解完这些代码的实际作用之后就可以在终端中运行webpack --config build/webpack.config.js命令。webpack会将源代码编译到dist文件夹中,这个文件夹中的内容就可用在开发者工具中运行、预览、上传。

优化

完成了最基础的webpack构建策略后,我们实现了apphome页面的转化,但这还远远不够。我们还需要解决许多的问题:

  • 页面文件增多怎么办,组件怎么处理
  • 预期的预编译如何做
  • 规范如何结合到工程中
  • 环境变量怎么处理

接下来我们针对以上几点进行webpack策略的升级:

页面与组件

一开始我的实现方法是写一个工具函数利用glob收集pages和components下的js文件然后生成入口对象传递给entry

/** build/entry-extract-plugin.js */const fs = require('fs');const path = require('path');const chalk = require('chalk');const replaceExt = require('replace-ext');const { difference } = require('lodash');const SingleEntryPlugin = require('webpack/lib/SingleEntryPlugin');const MultiEntryPlugin = require('webpack/lib/MultiEntryPlugin');class EntryExtractPlugin {  constructor() {    this.appContext = null;    this.pages = [];    this.entries = [];
  }  /**
  	*	收集app.json文件中注册的pages和subpackages生成一个待处理数组
  	*/
  getPages() {    const app = path.resolve(this.appContext, 'app.json');    const content = fs.readFileSync(app, 'utf8');    const { pages = [], subpackages = [] } = JSON.parse(content);    const { length: pagesLength } = pages;    if (!pagesLength) {      console.log(chalk.red('ERROR in "app.json": pages字段缺失'));
      process.exit();
    }    /** 收集分包中的页面 */
    const { length: subPackagesLength } = subpackages;    if (subPackagesLength) {
      subpackages.forEach((subPackage) => {        const { root, pages: subPages = [] } = subPackage;        if (!root) {          console.log(chalk.red('ERROR in "app.json": 分包配置中root字段缺失'));
          process.exit();
        }        const { length: subPagesLength } = subPages;        if (!subPagesLength) {          console.log(chalk.red(`ERROR in "app.json": 当前分包 "${root}" 中pages字段为空`));
          process.exit();
        }
        subPages.forEach((subPage) => pages.push(`${root}/${subPage}`));
      });
    }    return pages;
  }  /**
  	*	以页面为起始点递归去寻找所使用的组件
  	*	@param {String} 当前文件的上下文路径
  	*	@param {String} 依赖路径
  	* @param {Array} 包含全部入口的数组
  	*/
  addDependencies(context, dependPath, entries) {    /** 生成绝对路径 */
    const isAbsolute = dependPath[0] === '/';    let absolutePath = '';    if (isAbsolute) {
      absolutePath = path.resolve(this.appContext, dependPath.slice(1));
    } else {
      absolutePath = path.resolve(context, dependPath);
    }    /** 生成以源代码目录为基准的相对路径 */
    const relativePath = path.relative(this.appContext, absolutePath);    /** 校验该路径是否合法以及是否在已有入口当中 */
    const jsPath = replaceExt(absolutePath, '.js');    const isQualification = fs.existsSync(jsPath);    if (!isQualification) {      console.log(chalk.red(`ERROR: in "${replaceExt(relativePath, '.js')}": 当前文件缺失`));
      process.exit();
    }    const isExistence = entries.includes((entry) => entry === absolutePath);    if (!isExistence) {
      entries.push(relativePath);
    }    /** 获取json文件内容 */
    const jsonPath = replaceExt(absolutePath, '.json');    const isJsonExistence = fs.existsSync(jsonPath);    if (!isJsonExistence) {      console.log(chalk.red(`ERROR: in "${replaceExt(relativePath, '.json')}": 当前文件缺失`));
      process.exit();
    }    try {      const content = fs.readFileSync(jsonPath, 'utf8');      const { usingComponents = {} } = JSON.parse(content);      const components = Object.values(usingComponents);      const { length } = components;      /** 当json文件中有再引用其他组件时执行递归 */
      if (length) {        const absoluteDir = path.dirname(absolutePath);
        components.forEach((component) => {          this.addDependencies(absoluteDir, component, entries);
        });
      }
    } catch (e) {      console.log(chalk.red(`ERROR: in "${replaceExt(relativePath, '.json')}": 当前文件内容为空或书写不正确`));
      process.exit();
    }
  }  /**
  	* 将入口加入到webpack中
  	*/
  applyEntry(context, entryName, module) {    if (Array.isArray(module)) {      return new MultiEntryPlugin(context, module, entryName);
    }    return new SingleEntryPlugin(context, module, entryName);
  }

  apply(compiler) {    /** 设置源代码的上下文 */
    const { context } = compiler.options;    this.appContext = context;

    compiler.hooks.entryOption.tap('EntryExtractPlugin', () => {      /** 生成入口依赖数组 */
      this.pages = this.getPages();      this.pages.forEach((page) => void this.addDependencies(context, page, this.entries));      this.entries.forEach((entry) => {        this.applyEntry(context, entry, `./${entry}`).apply(compiler);
      });
    });

    compiler.hooks.watchRun.tap('EntryExtractPlugin', () => {      /** 校验页面入口是否增加 */
      const pages = this.getPages();      const diffPages = difference(pages, this.pages);      const { length } = diffPages;      if (length) {        this.pages = this.pages.concat(diffPages);        const entries = [];        /** 通过新增的入口页面建立依赖 */
        diffPages.forEach((page) => void this.addDependencies(context, page, entries));        /** 去除与原有依赖的交集 */
        const diffEntries = difference(entries, this.entries);
        diffEntries.forEach((entry) => {          this.applyEntry(context, entry, `./${entry}`).apply(compiler);
        });        this.entries = this.entries.concat(diffEntries);
      }
    });
  }
}module.exports = EntryExtractPlugin;复制代码
/** build/webpack.config.js */const EntryExtractPlugin = require('./entry-extract-plugin');module.exports = {
  ...
  entry: {    app: './app.js'
  },  plugins: [
    ...
    new EntryExtractPlugin()
  ]
}复制代码

위 코드를 작성한 후 위 코드의 기능을 설명하겠습니다. 🎜
    🎜app을 대상으로 항목 <code>entry 개체에 두 개의 속성을 썼습니다. js 및 home/index.js는 webpack의 구성 진입점 역할을 합니다. 이 파일을 시작점으로 사용하여 해당 종속성을 생성하므로 항목 파일 다른 파일이 포함되어 있는 경우 가져온 파일도 webpack에서 처리할 수 있습니다. 🎜🎜모듈에서는 babel-loader를 사용하여 js 파일을 ES6에서 ES5로 변환하고 새로운 구문 처리를 추가했습니다. 이런 방식으로 우리는 네이티브 소형 프로그램 개발 시 항상 regenerator-runtime을 반복적으로 도입해야 하는 문제를 해결합니다. (이 단계에서는 @babel/core, @babel/preset-env, @babel/plugin-transform-runtime을 설치해야 합니다. @babel/runtime, babel-loader 이러한 종속성) 🎜🎜재처리할 필요가 없는 파일을 처리하려면 copy-webpack-plugin을 사용하세요. . 이 플러그인은 파일을 대상 디렉터리에 직접 복사할 수 있습니다. 🎜
🎜이 코드의 실제 기능을 이해한 후에는 터미널에서 webpack --config build/webpack.config.js 명령을 실행할 수 있습니다. webpack은 소스 코드를 dist 폴더로 컴파일하고, 이 폴더의 콘텐츠는 개발자 도구에서 실행하고, 미리 보고, 업로드할 수 있습니다. 🎜

최적화🎜🎜가장 기본적인 웹팩 구성 전략을 완료한 후 apphomeConversion을 구현했습니다. 하지만 이것만으로는 충분하지 않습니다. 여전히 많은 문제를 해결해야 합니다. 🎜🎜🎜페이지 파일이 늘어나면 어떻게 해야 하는지, 구성 요소를 어떻게 처리해야 하는지 🎜🎜예상 사전 컴파일을 어떻게 해야 하는지 🎜🎜사양을 프로젝트에 통합하는 방법 🎜🎜환경 변수를 처리하는 방법 🎜🎜🎜 다음으로 위의 사항에 중점을 둘 것입니다. webpack 전략을 업그레이드하기 위한 몇 가지 사항: 🎜

페이지 및 구성 요소

🎜제 초기 구현 방법은 다음과 같습니다. glob을 사용하는 도구 함수를 작성하려면 페이지 및 구성 요소 아래에 있는 js 파일을 수집한 다음 항목 개체를 생성하고 entry에 전달합니다. 하지만 실제로는 이 접근 방식에 두 가지 단점이 있다는 사실을 발견했습니다. 🎜
  1. 当终端中已经启动了命令,这时候新增页面或组件都不会自动生成新的入口,也就是我们要重跑一遍命令。
  2. 工具函数写死了匹配pages和components文件夹下的文件,不利于项目的延展性,如果我们需要分包或者文件夹命名需要改动时,我们就需要改动工具函数。

本着程序员应该是极度慵懒,能交给机器完成的事情绝不自己动手的信条,我开始研究新的入口生成方案。最终确定下来编写一个webpack的插件,在webpack构建的生命周期中生成入口,废话不多说上代码:

/** build/entry-extract-plugin.js */const fs = require(&#39;fs&#39;);const path = require(&#39;path&#39;);const chalk = require(&#39;chalk&#39;);const replaceExt = require(&#39;replace-ext&#39;);const { difference } = require(&#39;lodash&#39;);const SingleEntryPlugin = require(&#39;webpack/lib/SingleEntryPlugin&#39;);const MultiEntryPlugin = require(&#39;webpack/lib/MultiEntryPlugin&#39;);class EntryExtractPlugin {  constructor() {    this.appContext = null;    this.pages = [];    this.entries = [];
  }  /**
  	*	收集app.json文件中注册的pages和subpackages生成一个待处理数组
  	*/
  getPages() {    const app = path.resolve(this.appContext, &#39;app.json&#39;);    const content = fs.readFileSync(app, &#39;utf8&#39;);    const { pages = [], subpackages = [] } = JSON.parse(content);    const { length: pagesLength } = pages;    if (!pagesLength) {      console.log(chalk.red(&#39;ERROR in "app.json": pages字段缺失&#39;));
      process.exit();
    }    /** 收集分包中的页面 */
    const { length: subPackagesLength } = subpackages;    if (subPackagesLength) {
      subpackages.forEach((subPackage) => {        const { root, pages: subPages = [] } = subPackage;        if (!root) {          console.log(chalk.red(&#39;ERROR in "app.json": 分包配置中root字段缺失&#39;));
          process.exit();
        }        const { length: subPagesLength } = subPages;        if (!subPagesLength) {          console.log(chalk.red(`ERROR in "app.json": 当前分包 "${root}" 中pages字段为空`));
          process.exit();
        }
        subPages.forEach((subPage) => pages.push(`${root}/${subPage}`));
      });
    }    return pages;
  }  /**
  	*	以页面为起始点递归去寻找所使用的组件
  	*	@param {String} 当前文件的上下文路径
  	*	@param {String} 依赖路径
  	* @param {Array} 包含全部入口的数组
  	*/
  addDependencies(context, dependPath, entries) {    /** 生成绝对路径 */
    const isAbsolute = dependPath[0] === &#39;/&#39;;    let absolutePath = &#39;&#39;;    if (isAbsolute) {
      absolutePath = path.resolve(this.appContext, dependPath.slice(1));
    } else {
      absolutePath = path.resolve(context, dependPath);
    }    /** 生成以源代码目录为基准的相对路径 */
    const relativePath = path.relative(this.appContext, absolutePath);    /** 校验该路径是否合法以及是否在已有入口当中 */
    const jsPath = replaceExt(absolutePath, &#39;.js&#39;);    const isQualification = fs.existsSync(jsPath);    if (!isQualification) {      console.log(chalk.red(`ERROR: in "${replaceExt(relativePath, &#39;.js&#39;)}": 当前文件缺失`));
      process.exit();
    }    const isExistence = entries.includes((entry) => entry === absolutePath);    if (!isExistence) {
      entries.push(relativePath);
    }    /** 获取json文件内容 */
    const jsonPath = replaceExt(absolutePath, &#39;.json&#39;);    const isJsonExistence = fs.existsSync(jsonPath);    if (!isJsonExistence) {      console.log(chalk.red(`ERROR: in "${replaceExt(relativePath, &#39;.json&#39;)}": 当前文件缺失`));
      process.exit();
    }    try {      const content = fs.readFileSync(jsonPath, &#39;utf8&#39;);      const { usingComponents = {} } = JSON.parse(content);      const components = Object.values(usingComponents);      const { length } = components;      /** 当json文件中有再引用其他组件时执行递归 */
      if (length) {        const absoluteDir = path.dirname(absolutePath);
        components.forEach((component) => {          this.addDependencies(absoluteDir, component, entries);
        });
      }
    } catch (e) {      console.log(chalk.red(`ERROR: in "${replaceExt(relativePath, &#39;.json&#39;)}": 当前文件内容为空或书写不正确`));
      process.exit();
    }
  }  /**
  	* 将入口加入到webpack中
  	*/
  applyEntry(context, entryName, module) {    if (Array.isArray(module)) {      return new MultiEntryPlugin(context, module, entryName);
    }    return new SingleEntryPlugin(context, module, entryName);
  }

  apply(compiler) {    /** 设置源代码的上下文 */
    const { context } = compiler.options;    this.appContext = context;

    compiler.hooks.entryOption.tap(&#39;EntryExtractPlugin&#39;, () => {      /** 生成入口依赖数组 */
      this.pages = this.getPages();      this.pages.forEach((page) => void this.addDependencies(context, page, this.entries));      this.entries.forEach((entry) => {        this.applyEntry(context, entry, `./${entry}`).apply(compiler);
      });
    });

    compiler.hooks.watchRun.tap(&#39;EntryExtractPlugin&#39;, () => {      /** 校验页面入口是否增加 */
      const pages = this.getPages();      const diffPages = difference(pages, this.pages);      const { length } = diffPages;      if (length) {        this.pages = this.pages.concat(diffPages);        const entries = [];        /** 通过新增的入口页面建立依赖 */
        diffPages.forEach((page) => void this.addDependencies(context, page, entries));        /** 去除与原有依赖的交集 */
        const diffEntries = difference(entries, this.entries);
        diffEntries.forEach((entry) => {          this.applyEntry(context, entry, `./${entry}`).apply(compiler);
        });        this.entries = this.entries.concat(diffEntries);
      }
    });
  }
}module.exports = EntryExtractPlugin;复制代码

由于webpack的plugin相关知识不在我们这篇文章的讨论范畴,所以我只简单的介绍一下它是如何介入webpack的工作流程中并生成入口的。(如果有兴趣想了解这些可以私信我,有时间的话可能会整理一些资料出来给大家)该插件实际做了两件事:

  1. 通过compiler的entryOption钩子,我们将递归生成的入口数组一项一项的加入entry中。
  2. 通过compiler的watchRun钩子监听重新编译时是否有新的页面加入,如果有就会以新加入的页面生成一个依赖数组,然后再加入entry中。

现在我们将这个插件应用到之前的webpack策略中,将上面的配置更改为:(记得安装chalk replace-ext依赖)

/** build/webpack.config.js */const EntryExtractPlugin = require(&#39;./entry-extract-plugin&#39;);module.exports = {
  ...
  entry: {    app: &#39;./app.js&#39;
  },  plugins: [
    ...
    new EntryExtractPlugin()
  ]
}复制代码

样式预编译与EsLint

样式预编译和EsLint应用其实已经有许多优秀的文章了,在这里我就只贴出我们的实践代码:

/** build/webpack.config.js */const MiniCssExtractPlugin = require(&#39;mini-css-extract-plugin&#39;);module.exports = {
  ...
  module: {    rules: [
      ...
      {        enforce: &#39;pre&#39;,        test: /\.js$/,        exclude: /node_modules/,        loader: &#39;eslint-loader&#39;,        options: {          cache: true,          fix: true,
        },
      },
      {        test: /\.less$/,        use: [
          {            loader: MiniCssExtractPlugin.loader,
          },
          {            loader: &#39;css-loader&#39;,
          },
          {            loader: &#39;less-loader&#39;,
          },
        ],
      },
    ]
  },  plugins: [
    ...
    new MiniCssExtractPlugin({ filename: &#39;[name].wxss&#39; })
  ]
}复制代码

我们修改完策略后就可以将wxss后缀名的文件更改为less后缀名(如果你想用其他的预编译语言,可以自行修改loader),然后我们在js文件中加入import './index.less'语句就能看到样式文件正常编译生成了。样式文件能够正常的生成最大的功臣就是mini-css-extract-plugin工具包,它帮助我们转换了后缀名并且生成到目标目录中。

环境切换

环境变量的切换我们使用cross-env工具包来进行配置,我们在package.json文件中添加两句脚本命令:

"scripts": {
	"dev": "cross-env OPERATING_ENV=development webpack --config build/webpack.config.js --watch",
	"build": "cross-env OPERATING_ENV=production webpack --config build/webpack.config.js
}复制代码

相应的我们也修改一下webpack的配置文件,将我们应用的环境也告诉webpack,这样webpack会针对环境对代码进行优化处理。

/** build/webpack.config.js */const { OPERATING_ENV } = process.env;module.exports = {
  ...
  mode: OPERATING_ENV,  devtool: OPERATING_ENV === &#39;production&#39; ? &#39;source-map&#39; : &#39;inline-source-map&#39;}复制代码

虽然我们也可以通过命令为webpack设置mode,这样也可以在项目中通过process.env.NODE_ENV访问环境变量,但是我还是推荐使用工具包,因为你可能会有多个环境uat test pre等等。

针对JS优化

小程序对包的大小有严格的要求,单个包的大小不能超过2M,所以我们应该对JS做进一步的优化,这有利于我们控制包的大小。我所做的优化主要针对runtime和多个入口页面之间引用的公共部分,修改配置文件为:

/** build/webpack.config.js */module.exports = {
  ...
  optimization: {    splitChunks: {      cacheGroups: {        commons: {          chunks: &#39;initial&#39;,          name: &#39;commons&#39;,          minSize: 0,          maxSize: 0,          minChunks: 2,
        },
      },
    },    runtimeChunk: {      name: &#39;manifest&#39;,
    },
  },
}复制代码

webpack会将公共的部分抽离出来在dist文件夹根目录中生成common.jsmanifest.js文件,这样整个项目的体积就会有明显的缩小,但是你会发现当我们运行命令是开发者工具里面项目其实是无法正常运行的,这是为什么?

这主要是因为这种优化使小程序其他的js文件丢失了对公共部分的依赖,我们对webpack配置文件做如下修改就可以解决了:

/** build/webpack.config.js */module.exports = {
  ...
  output: {
    ...
    globalObject: &#39;global&#39;
  },  plugins: [    new webpack.BannerPlugin({      banner: &#39;const commons = require("./commons");\nconst runtime = require("./runtime");&#39;,      raw: true,      include: &#39;app.js&#39;,
    })
  ]
}复制代码

相关学习推荐:js视频教程

小小解惑

许多读者可能会有疑惑,为什么你不直接使用已有的框架进行开发,这些能力已经有许多框架支持了。选择框架确实是一个不错的选择,毕竟开箱即用为开发者带来了许多便利。但是这个选择是有利有弊的,我也对市面上的较流行框架做了一段时间的研究和实践。较为早期的腾讯的wepy、美团的mpvue,后来者居上的京东的taro、Dcloud的uni-app等,这些在应用当中我认为有以下一些点不受我青睐:

  • 블랙박스는 때때로 문제가 우리 자신의 코드에 있는지 아니면 프레임워크의 컴파일 프로세스에 있는지 찾기 어렵게 만듭니다(이로 인해 많은 함정에 빠지게 되었습니다)
  • 주변에서 사용할 수 있는 리소스 UI 등 프레임워크가 제한적입니다. 기본적으로 개발 지원을 공식 팀에 의존하여 사용합니다. 커뮤니티가 없으면 필요한 리소스를 찾기가 매우 어렵습니다. (유니앱 커뮤니티가 이 점에 있어서는 잘했다고 생각합니다. )
  • 일부 기존 네이티브 리소스와 결합할 수 없습니다. 이러한 프레임워크는 기본적으로 컴파일 원칙을 기반으로 하며 개발 언어로 React 또는 Vue를 사용하는 기능을 제공하므로 네이티브 리소스에 원활하게 액세스하기가 어렵습니다. 회사에 일부 비즈니스 구성 요소가 축적되어 있으면 두통이 생길 것입니다.)
  • 마지막 점은 제가 가장 걱정하는 점이기도 합니다. 프레임워크의 업그레이드 속도가 기존 프로젝트보다 뒤처지면 어떻게 해야 할까요? 프로그램 엔지니어링을 하는 이유 (사실 또 다른 궁금증이 있죠 ㅎㅎ)
Written at the end

위는 제가 엔지니어링 네이티브 소규모 프로그램을 탐색한 내용이고, 관련 스타일 사양도 적용해봤습니다. 내 팀에서는 이 기사에서 구체적으로 언급하지 않았습니다. 관심이 있는 경우 내 칼럼의 "팀 규범 - 스타일 표준 관행" 기사를 확인하실 수 있습니다. 실제로 정적 리소스 관리와 프로젝트 디렉터리 추가도 팀의 필요에 따라 개선되고 보완될 수 있습니다. 이 글이 이 분야에서 연습이 필요한 팀들에게 도움이 되기를 바랍니다. 잘못된 의견이나 개선이 필요한 부분이 있다면 댓글로 알려주세요.

위 내용은 실용장---WeChat 미니 프로그램 엔지니어링 탐색 웹팩의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 juejin.im에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제