vuepress打包部署踩坑及解决

发布时间:

这篇文章主要介绍了vuepress打包部署踩坑及解决方案,具有很好的参考价值,希望对大家有所帮助。

vuepress打包部署踩坑记录

官网教程

官网给了多种部署方案,这里我才用的是部署到github上

在你的项目中,创建一个如下的 deploy.sh 文件(请自行判断去掉高亮行的注释):

#!/usr/bin/env sh
# 确保脚本抛出遇到的错误
set -e
# 生成静态文件
npm run docs:build
# 进入生成的文件夹
cd docs/.vuepress/dist
# 如果是发布到自定义域名
# echo 'www.example.com' > CNAME
git init
git add -A
git commit -m 'deploy'
# 如果发布到 https://<USERNAME>.github.io
# git push -f git@github.com:<USERNAME>/<USERNAME>.github.io.git main
# 如果发布到 https://<USERNAME>.github.io/<REPO>
/**这个地方换成自己的github地址和仓库地址**/
git push -f git@github.com:FightingN/vue-common-vuepress.git master:gh-pages
cd -

然后需要注意的是 .vuepress/config.js文件的base需要修改一下

base: '/vue-common-vuepress/',

然后运行npm run deploy就可以正常打包到github托管中了

vuepress打包报错:error Error rendering /:

问题

使用vuepress写文档网站,为了实现element-ui类似的组件预览效果,项目里面将ant-design-vue和基于ant-design-vue的自己封装的组件引入项目中,开发环境能正常运行。当运行Build打包后,报错:error Error rendering /:

vuepress打包部署踩坑及解决

方案1

经查询vuepress github issuse 得到的答案是,vuepress是服务端渲染的,浏览器的 API 访问限制。在页面加载第三方组件的时候,可能出现找不到的情况,建议在当前页面使用时再引入。

vuepress打包部署踩坑及解决

内容链接

当然,如果不需要组件预览效果,及:::demo标志,就不会报错。需要该效果时,按照官网的做法,使用component标签,在需要的md 代码块里面动态加载组件,可以解决该问题

 mounted(){
  import("my-component").then(myComponent=>{
  console.log("myComponent", myComponent)
  this.dynamicComponent = myComponent.Tree
  })
  },

当然还有一种方法就是在mounted里面import组件并且注册组件,template部分照常使用之前的标签

<template>
	<my-tree/>
</template>
<script>
import Vue from "vue"
	export default {
		mounted(){
		 import("my-component").then(myComponent=>{
  console.log("myComponent", myComponent)
  Vue.component("myTree",myComponent.Tree)
  })
		}
	}
</script>

然而运行后,报错my-tree没有注册。

Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the “name” option.

found in

方案2

方案1,问题看是解决了,没有报错了,但是。这不是我们想要的。

本质需求是实现组件库的组件预览效果,并且能复制代码,方案1能预览,但是代码都变成了component标签,不符合需求。

接下来我们想排查下是什么组件影响了打包:

采用本地建测试仓库,本地建测试组件库,本地建测试文档项目,逐个移植原组件库的组件到测试组件库中,发布到测试仓库后,更新测试文档,然后执行本地预览和打包命令。最终找出影响打包的组件有dialog,uploadAvatar,preview,cropper-img等组件。这些组件的共同点就是都直接或间接用到了document操作dom,还有其他window的方法,或者bom的方法事件,或者在组件内注册第三方组件,如本项目中就用的atob,btoa,这个也是造成出现该错误的原因之一。经在upoadAvatar去掉document得到验证。目前除了这几个组件,其他均可以实现组件预览效果。但是这几个组件只要引入就报错,也影响了全局。

最终解决

知道了问题所在,结合上面的思路,总之的一点就是想要用window或bom方法事件,一定要在mounted后执行。这里来一个一个看。首先看dialog组件,之所以会报错就是dialog组件在created生命周期的时候用了document(下面只展示关键代码,多余的代码略)

import { VNode } from "vue"
import { Component, Prop, Vue, Watch } from "vue-property-decorator"
import { dialogConfig } from "./type"
export default class Dialog extends Vue {
  created() {
this.dialogInit()
this.getWindowSize()
  }
  /**
   * 弹窗初始化
   * @private
   */
  private dialogInit():void {
   this.id = "lc-dialog" + (Math.random() * 1000000 + new Date().getTime()).toString(16).split(".")[0]
this.$nextTick(() => {
  const dialogDom:any = document.getElementById(that.id)
  if (!dialogDom) {
return
  }
  this.dialogDom = dialogDom
  this.headerDom = dialogDom.querySelector(".ant-modal-header")
  this.footerDom = dialogDom.querySelector(".ant-modal-footer")
})
  }
  /**
   * 获取窗口尺寸
   * @private
   */
  private getWindowSize():void {
const that = this as any
this.windowSize = {
  windowWidth: document.documentElement.clientWidth,
  windowHeight: document.documentElement.clientHeight
}
// // console.log(that.dialogScreen)
// // console.log(that.isScreen)
  window.onresize = () => {
  if (this.dialogScreen && this.isScreen) {
clearTimeout(this.debounceTimeOut)
this.debounceTimeOut = setTimeout(() => {
  this.windowSize = {
windowWidth: document.documentElement.clientWidth,
windowHeight: document.documentElement.clientHeight
  }
}, 300)
  }
}
  }
  render (h:any):VNode {
...
 }
}

那这里就很简单,将created改成mounted就行

再来看看图片裁剪组件,之前的代码是

<!--图片裁剪-->
<template>
  <lc-dialog
:title="title"
:visible.sync="visible"
:width="800"
:footer="null"
ref="cropperModal"
:before-close="beforeClose"
:z-index="zIndex"
  >
  <vue-cropper
ref="cropper"
:img="img"
:info="true"
:auto-crop="options.autoCrop"
:output-type="config.outputType || (config.originFile ? config.originFile.type : '')"
:auto-crop-width="options.autoCropWidth"
:auto-crop-height="options.autoCropHeight"
:fixed-box="options.fixedBox"
@real-time="realTime"
  >
  </vue-cropper>
  </lc-dialog>
</template>
<script>
import Vue from "vue"
import VueCropper from "vue-cropper"
import { getZIndexByDiv } from "../../utils/lc-methods"
Vue.use(VueCropper)
export default {
  name: "CropperImg",
  ...
}
</script>

这里需要解决的是vue-cropper组件的按需引入问题。按照vue-press的思路,可以使用动态import,于是开始尝试如下:

<!--图片裁剪-->
<template>
  <lc-dialog
  :title="title"
  :visible.sync="visible"
  :width="800"
  :footer="null"
  ref="cropperModal"
  :before-close="beforeClose"
  :z-index="zIndex"
  >
<component
  v-if="component"
  :is="component"
  ref="cropper"
  :img="img"
  :info="true"
  :auto-crop="options.autoCrop"
  :output-type="config.outputType || (config.originFile ? config.originFile.type : '')"
  :auto-crop-width="options.autoCropWidth"
  :auto-crop-height="options.autoCropHeight"
  :fixed-box="options.fixedBox"
  @real-time="realTime"
  />
  </lc-dialog>
</template>
<script>
import { getZIndexByDiv } from "../../utils/lc-methods"
export default {
  name: "CropperImg",
  mounted() {
  import("vue-cropper").then(VueCropperModule => {
	this.component = VueCropperModule.VueCropper
	} 
 },
 ...
}
</script>

然而运行后发现,没有生效。于是将import(“vue-cropper”).catch打印发现竟然报错了。vuepress是走node服务渲染模式,也可能和nodejs 服务端渲染不支持import有关,于是改成require引入,如下

<!--图片裁剪-->
<template>
  <lc-dialog
  :title="title"
  :visible.sync="visible"
  :width="800"
  :footer="null"
  ref="cropperModal"
  :before-close="beforeClose"
  :z-index="zIndex"
  >
<component
  v-if="component"
  :is="component"
  ref="cropper"
  :img="img"
  :info="true"
  :auto-crop="options.autoCrop"
  :output-type="config.outputType || (config.originFile ? config.originFile.type : '')"
  :auto-crop-width="options.autoCropWidth"
  :auto-crop-height="options.autoCropHeight"
  :fixed-box="options.fixedBox"
  @real-time="realTime"
  />
  </lc-dialog>
</template>
<script>
import { getZIndexByDiv } from "../../utils/lc-methods"
export default {
  name: "CropperImg",
  mounted() {
const VueCropperModule = require("vue-cropper")
// console.log("VueCropperModule", VueCropperModule)
this.component = VueCropperModule.VueCropper
  },
 ...
}
</script>

结果成功,引入该组件后,也不会再报错error Error rendering /:

然后看看头像上传组件,这个也比较坑。这个组件mounted没有document,也没引入第三方组件。无奈,只有使用注释大法逐步查找,最后确定是组件内部引入压缩图片的方法造成

import { compressImageFun } from "../../utils/img-method"

发现只要组件引入了该方法,不管是否使用,都会报错,而注释该方法就万事大吉。

于是取查找compressImageFun相关,开始因为无头绪,同样只能使用注释大法,最后查到罪魁祸首是atob,btoa函数。这样那么解决办法和引入第三方组件类似。compressImageFun里面不需要动,改引入的地方就行,如下

export default {
  name: "UploadAvatar",
  data () {
return {
  compressImageFun: null, // 压缩函数注意这里因为无法直接使用compressImageFun,所以需要存在data中使用
}
  },
  mounted() {
const imgMethod = require("../../utils/img-method")
this.compressImageFun = imgMethod.compressImageFun
  },
  methods: {
   
/**
 * 上传之前
 * @param file
 * @returns {boolean}
 */
async beforeUpload (file) {
  return new Promise(async (resolve, reject) => {
  ...省略若干代码
if (!this.noCompress) {
  newCompressFile = await this.compressImageFun({ file: cropperFile || file })
  cropperFile = newCompressFile.data
}
 ...
  })
},
}

至此,该错误被攻克!!!