uni-app脚手架踩坑记(下)

简介: 最近在做跨平台框架的试点,选择了uni-app,并打算先在h5上开始试点。 由于uni-app提供的基于vue-cli的脚手架与我们内部的脚手架稍有些不同,直接使用稍微有点学习成本,所以fork了一下,稍作修改,做了一个内部版本的脚手架(主要就是将publicPath从manifest.json中拿出来,实现动态配置),目的就是让其用起来和我们自己的脚手架差不多。

重新build,确实生效了,看一下生成的代码大概是这样的:


return createElement("v-uni-image", { 
  staticClass: "logo", 
  attrs: { 
    src: require(// ... ) 
  }
})


关于transformAssetUrls



趁热打铁,又看一下vue-loader的源码,看看到底是如何处理transformAssetUrls这个属性的,一些关键代码:


// 详见 https://github.com/vuejs/vue-loader/blob/master/lib/loaders/templateLoader.js#L32
  const { compileTemplate } = require('@vue/component-compiler-utils')
// ...
// for vue-component-compiler
// 最终将传递给模板编译器的所有选项
  const finalOptions = {
    source,
    filename: this.resourcePath,
    compiler,
    compilerOptions,
    // allow customizing behavior of vue-template-es2015-compiler
    transpileOptions: options.transpileOptions,
    transformAssetUrls: options.transformAssetUrls || true,  // 注意这里!!!
    isProduction,
    isFunctional,
    optimizeSSR: isServer && options.optimizeSSR !== false,
    prettify: options.prettify
  }
  const compiled = compileTemplate(finalOptions)  // 将所有的选项传递给compileTemplate模板编译器
  // ...


追下去:


// 详见 https://github.com/vuejs/component-compiler-utils/blob/master/lib/compileTemplate.ts#L113
  import assetUrlsModule from './templateCompilerModules/assetUrl'
  // ...
  let finalCompilerOptions = finalOptions
  if (transformAssetUrls) { // 如果传入了自定义的transformAssetUrls,将其与默认的合并
    const builtInModules = [
      transformAssetUrls === true
        ? assetUrlsModule()
        : assetUrlsModule(transformAssetUrls),
      srcsetModule()    
    ]
    finalCompilerOptions = Object.assign({}, compilerOptions, {
      modules: [...builtInModules, ...(compilerOptions.modules || [])] // 是不是很眼熟
    })
  }
  const { render, staticRenderFns, tips, errors } = compile(
    source,
    finalCompilerOptions
  )


继续追:


// 详见 https://github.com/vuejs/component-compiler-utils/blob/master/lib/templateCompilerModules/assetUrl.ts
// 熟悉的面孔
const defaultOptions: AssetURLOptions = {
  video: ['src', 'poster'],
  source: 'src',
  img: 'src',
  image: ['xlink:href', 'href'],
  use: ['xlink:href', 'href']
}
// 原来是通过返回一个postTransformNode来处理上面这些标签的
export default (userOptions?: AssetURLOptions) => {
  const options = userOptions
    ? Object.assign({}, defaultOptions, userOptions)
    : defaultOptions
  return {
    postTransformNode: (node: ASTNode) => {
      transform(node, options)
    }
  }
}
function transform(node: ASTNode, options: AssetURLOptions) {
  for (const tag in options) {
    // ...
    attributes.forEach(item => node.attrs.some(attr => rewrite(attr, item)))
  }
}
function rewrite(attr: Attr, name: string) {
  if (attr.name === name) {
    // ... 大概是这个意思
    attr.value = `require(${attr.value})`
  }
  return false
}


可以看到,原来transformAssetUrls里面的选项会直接生成一个叫的postTransformNode的钩子,他的作用就是用来处理模板template里面的每一个元素element,生成单独的语法树节点ASTNode,并在ASTNode被进一步处理之后要执行的。与postTransformNode对应的还有preTransformNode钩子,顾名思义,就是在生成的ASTNode即将被进一步处理之前要执行的钩子。这两类钩子可以放到一个 { modules: [ 钩子 ] } 的对象中,一并传入给最终的模板编译器。


而在uni-app的自定义编译器的编译器选项@dcloudio/vue-cli-plugin-uni/lib/h5/compiler-options.js`中,也可以看到类似的代码:


// @dcloudio/vue-cli-plugin-uni/lib/h5/compiler-options.js#L113
module.exports = {
  isUnaryTag,
  preserveWhitespace: false,
  modules: [require('../format-text'), {
    preTransformNode (el, {
      warn
    }) {
        // ...
    },
    postTransformNode (el, {
      warn,
      filterModules
    }) {
        // ...
    }


uni-app自有组件的v-uni-前缀就是通过这种方式添加的。所以,上面的遇到问题也可以直接在这里比较暴力的方式处理:


// @dcloudio/vue-cli-plugin-uni/lib/h5/compiler-options.js#L113
// 将vue自带的处理方法引进来
const assetUrlsModule = require('@vue/component-compiler-utils/dist/templateCompilerModules/assetUrl').default
// 生成标签处理的钩子
const builtInModules = assetUrlsModule({ 'v-uni-image': 'src' })
module.exports = {
  isUnaryTag,
  preserveWhitespace: false,
  modules: [require('../format-text'), {
    ...builtInModules,
  }, {
    preTransformNode (el, {
      warn
    }) {
        // ...
    },
    postTransformNode (el, {
      warn,
      filterModules
    }) {
        // ...
    }


更多关于modules的信息可参考: 编译器模块的数组


直接在项目中的解决办法


如果想直接使用官方的脚手架来解决这个问题,就可以在vue.config.js中加入如下代码来解决:


module.exports = {
  chainWebpack(webpackConfig) {
    webpackConfig.module
    .rule('vue')
    .test([/\.vue$/, /\.nvue$/])
    .use('vue-loader')
    .tap(options => Object.assign(options, {
      transformAssetUrls: {
        'v-uni-image': 'src'
      }
    }))
    .end()
  },
  configureWebpack (config) {
    // ...blablabla  
  },
}


嗯,不是办法的办法。


你如果有更好的解决方法欢迎评论区留言,讨论。


相关文章
|
3月前
|
XML 前端开发 JavaScript
react学习笔记一:入门级小白到脚手架(create-react-app)开发项目
这篇文章是React的学习笔记,覆盖了从React的基础用法到高级特性,包括组件化、状态管理、生命周期、虚拟DOM等主题,适合React初学者参考。
121 0
react学习笔记一:入门级小白到脚手架(create-react-app)开发项目
|
3月前
|
移动开发 小程序 数据可视化
基于npm CLI脚手架的uniapp项目创建、运行与打包全攻略(微信小程序、H5、APP全覆盖)
基于npm CLI脚手架的uniapp项目创建、运行与打包全攻略(微信小程序、H5、APP全覆盖)
424 3
|
5月前
|
前端开发 JavaScript 中间件
React脚手架create-react-app简介
【8月更文挑战第13天】React脚手架create-react-app简介
292 4
|
JavaScript 小程序 开发者
使用脚手架创建uni-app项目(2)
使用脚手架创建uni-app项目
344 0
使用脚手架创建uni-app项目(2)
|
缓存 JavaScript 小程序
使用脚手架创建uni-app项目(1)
使用脚手架创建uni-app项目
467 0
使用脚手架创建uni-app项目(1)
|
移动开发 JavaScript
uni-app脚手架踩坑记(上)
最近在做跨平台框架的试点,选择了uni-app,并打算先在h5上开始试点。 由于uni-app提供的基于vue-cli的脚手架与我们内部的脚手架稍有些不同,直接使用稍微有点学习成本,所以fork了一下,稍作修改,做了一个内部版本的脚手架(主要就是将publicPath从manifest.json中拿出来,实现动态配置),目的就是让其用起来和我们自己的脚手架差不多。
|
JavaScript 前端开发 资源调度
官方 React 快速上手脚手架 create-react-app
此文简单讲解了官方 React 快速上手脚手架的安装与介绍。 1. React 快速上手脚手架 create-react-app 为了快速地进行构建使用 React 的项目,FaceBook 官方发布了一个无需配置的、用于快速构建开发环境的脚手架工具 create-react-app。
2202 0
|
9天前
|
开发框架 小程序 前端开发
圈子社交app前端+后端源码,uniapp社交兴趣圈子开发,框架php圈子小程序安装搭建
本文介绍了圈子社交APP的源码获取、分析与定制,PHP实现的圈子框架设计及代码编写,以及圈子小程序的安装搭建。涵盖环境配置、数据库设计、前后端开发与接口对接等内容,确保平台的安全性、性能和功能完整性。通过详细指导,帮助开发者快速搭建稳定可靠的圈子社交平台。
92 18
|
5天前
|
JSON 供应链 搜索推荐
淘宝APP分类API接口:开发、运用与收益全解析
淘宝APP作为国内领先的购物平台,拥有丰富的商品资源和庞大的用户群体。分类API接口是实现商品分类管理、查询及个性化推荐的关键工具。通过开发和使用该接口,商家可以构建分类树、进行商品查询与搜索、提供个性化推荐,从而提高销售额、增加商品曝光、提升用户体验并降低运营成本。此外,它还能帮助拓展业务范围,满足用户的多样化需求,推动电商业务的发展和创新。
22 5