如何上传大文件

image.png

server端

  1. 初始化文件( 分片文件 )上传的目录

    根据md5和fileName 创建一个文件夹

  2. 接收文件分片

    在接收文件分片的时候,我们首先会获取分片存储位置,然后计算分片的md5,然后将分片命名为${partIndex}|${partFileMd5}.part,存储到对应路径下。

  3. 合并文件分片

    拿到分片 list,根据index进行排序, 然后进行文件的合并, 使用流进行合并

  1. const writeStream = fse.createWriteStream(outputPath);
  2. const readStreamList = inputPathList.map((path) => {
  3. return fse.createReadStream(path);
  4. });
  5. return new Promise((resolve, reject) => {
  6. const multiStream = new MultiStream(readStreamList);
  7. multiStream.pipe(writeStream);
  8. multiStream.on('end', () => {
  9. fse.closeSync(fd);
  10. resolve(true);
  11. });
  12. multiStream.on('error', () => {
  13. fse.closeSync(fd);
  14. reject(false);
  15. });
  16. });
  17. }

client端

blob的slice 可以对文件进行切片, 然后 for循环 进行上传

如何做到断点续传

客户端维护一个 retryList 列表, 分片上传失败的时候, 加入 retryList 最后 上传这个retryList

  1. 每个分片有一个index值, 如果这个分片没传上去, 会在下一次对分片组进行排序 然后重新传

如何进行响应式布局

rem + 媒体查询(element-ui提供的 column 属性)

  1. 使用rem的时候需要在 css 加载之前, 定义 html font-size

markdown转为HTML,如何自定义风格

使用 markdown-it 这个开源库,将markdown文本转换成HTML,通过学习markdown-it-anchor的源码,自己写markdown-it 的插件,通过某个函数,在某个元素前面加自定义的类名,然后引入自定义的 CSS 样式即可

如何自定义编辑器


编辑器的功能有哪些

和普通的博客编辑器一样功能,还增加了mermaid流程图。 难点是如何显示粘贴的图片

复制的图片如何粘贴

  1. 前端监听粘贴事件
  2. 一旦发生粘贴事件,就将获取到的粘贴资源上传到后端服务器存起来
  3. 存好过后,后端返给前端链接,就可以查看了
  4. 将此图片写成markdown的形式

    同步滚动功能是怎么做的

    动态加载tag是怎么做的以及遇到了哪些困难

  5. 如何判断tag超了

  1. 根据 tag-wrapper 的高度进行判断
  1. 如何让 tag 只显示一行
  1. (1) 使用高度进行判断,如果超出了wrapper的高度,就给wrapper overflow: hidden 的属性
  2. 但问题是,无法统计超出的 tag 的数量
  3. (2) 如果高度超出了,将tag从最后,一个一个往前删除,直到高度没超为止。
  4. 很明显,这是一个重复调用的过程,在什么时机去执行这个函数,很重要
  5. 遇到的问题是,如果不在 nextTick 去执行,就不会生效
  1. 父组件的宽度动态变化应该如何去做
  1. 在子组件的mounted阶段,table 父组件的某一列的宽度还是未固定的, 导致的结果是父组件mounted生命周期之后, tag 全被折叠起来了。
  2. 解决办法是,在父组件的mounted周期之后去更新子组件,给子组件一个prop,在父组件的 mounted阶段更新这个prop,然后将子组件的渲染函数放到update生命周期中。然后给设置一个flag, 仅仅在第一次update 阶段才生效
  3. 然后监听 sizeChange 事件。

说一说鉴权功能

cookie-session

只要用户不禁用 cookie 就ok, 如果禁用cookie的话 就不行了

image.png

sso 给顶级域名设置cookie

JWT(JSON WEB TOKEN)

服务器返回一个包含用户信息和认证方式的token, 用户存在cookie中,然后在请求的时候带上,服务器再进行验证即可,这样 服务器就不需要存sessionId了

查询-渲染组件是怎么做的

  1. <template>
  2. <div>
  3. <slot name="table" :list="list"></slot>
  4. <!-- 作用域插槽,将父组件的数据传递给子组件 -->
  5. <pagination
  6. :total="total"
  7. :pageSize="pageSize"
  8. @preClick="handlePageChange"
  9. @nextClick="handlePageChange"
  10. @pageChange="handlePageChange"
  11. @pageSizeChange="handlePageSizeChange"
  12. ></pagination>
  13. </div>
  14. </template>
  15. <script>
  16. const obj = {
  17. params: '需要进行查询的参数信息',
  18. reqSource // 请求地址
  19. }
  20. export default defineComponent({
  21. name:"component",
  22. props: [obj, paginationProps, reqSource],
  23. setup() {
  24. const useTable = async obj => {
  25. const list = await getList(obj.params, obj.reqSource);
  26. watch(() => [obj.page, obj.pageSize], ([old1,new1],[old2,new2]) => {
  27. handlePageChange();
  28. handlePageSizeChange();
  29. });
  30. return {
  31. list
  32. }
  33. };
  34. return {
  35. list
  36. }
  37. }
  38. });
  39. </script>
  40. <style>
  41. </style>

excel 是怎么导出的

https://www.cnblogs.com/liuxianan/p/js-excel.html
http://blog.haoji.me/js-download.html#JS-dan-chu-xia-zai-dui-hua-kuang
JS要实现下载功能,一般都是这么几个过程:生成下载的URL,动态创建一个A标签,并将其href指向生成的URL,然后触发A标签的单击事件,这样就会弹出下载对话框,从而实现了一个下载的功能。
image.png
image.png
image.png

  1. function openDownloadDialog(url, saveName)
  2. {
  3. if(typeof url == 'object' && url instanceof Blob)
  4. {
  5. url = URL.createObjectURL(url); // 创建blob地址
  6. }
  7. var aLink = document.createElement('a');
  8. aLink.href = url;
  9. aLink.download = saveName || ''; // HTML5新增的属性,指定保存文件名,可以不要后缀,注意,file:///模式下不会生效
  10. var event;
  11. if(window.MouseEvent) event = new MouseEvent('click');
  12. else
  13. {
  14. event = document.createEvent('MouseEvents');
  15. event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
  16. }
  17. aLink.dispatchEvent(event);
  18. }

自定义loader 是怎么做的

router懒加载的loader

因为我们做的那个B端的页面有很多的路径,每个人做的模块各不相同,所以所关心的点也有所不同,为了提高首次进入的加载速度,会让用户自定义一个 router.config 的文件,写上自己工作所用的路由,然后webpack 在加载的时候,会去读取这个文件,生成最新的router文件,vue就去加载这个最新的router文件就可以了

一是为了应用隔离,二是为了提高加载速度
router中 index.ts

这里读取各个模块的文件就很费时间

  1. const context = require.context('./', true, /\.ts$/);
  2. let routerList = [];
  3. context.keys().forEach(key => {
  4. if (key !== './index.ts') {
  5. routerList = routerList.concat(context(key).default);
  6. }
  7. });
  8. export default routerList;
  1. config.module.rules.push({
  2. test: /\.ts$/,
  3. loader: path.join(__dirname, 'conf/lib/lazy-loader.js'),
  4. enforce: 'pre', // 优先处理
  5. include: resolve('src/router/config/index.ts'),
  6. options: {
  7. // laze-loader需要改写的文件地址
  8. baseUrl: resolve('src/router/config/index.ts'),
  9. baseModule: [...getLazyModuleConfig() || []], // 支持配置层面设置,格式:'./auth'
  10. },
  11. });
  12. module.exports = function (source) {} // 如何写loader,重要的是返回的source的内容是什么

常用组件加载的loader

因为这个应用中有很多的常用的组件,每次都要去加载的话会很麻烦,代码也会显得比较冗余。所以需要全局注册一些通用的组件。一个一个手动去注册又显得太麻烦

有没有一种方法,把需要进行全局注册的组件放在一个config 文件当中,然后去读取它,然后将其引入到main.ts 当中
答案就是自定义一个 loader,处理那个config文件,然后将处理的结果加入 main.ts 当中