精华内容
下载资源
问答
  • 不必翻墙,安装vue浏览器插件
    千次阅读
    2021-07-29 15:43:09

    Google chrome 浏览器开发vue项目时,需要安装vue浏览器插件。
    Vue.js devtools可以方便查看vue组件,events, 路由,vuex等信息。


    这是未安装时的*warning*

    warning



    这是浏览器扩展程序中的插件

    extensions


    步骤如下:

    1. 进入warning中提示的github仓库

    https://github.com/vuejs/vue-devtools

    devtool-git.png

    可以下载zip包,也可以clone仓库。这里我下载的压缩包。

    2. 解压后,在终端进入文件夹路径。

    Tips 解压后的文件夹名称一定要与package.json中的name一致。否则build报错。
    

    这里下载下来的文件夹名称是devtools-main,手动改成vue-devtools

    执行:
    cnpm install

    结束后,再执行:
    cnpm run build

    最后在浏览器扩展程序中,加载已解压的扩展程序

    选中vue-devtools/packages/shell-chrome

    添加成功!

    更多相关内容
  • vue开发浏览器插件

    2021-02-25 16:52:00
    vue-devtools-5.3.4.zip 适用于vue2.0 3.0未测试
  • vue开发浏览器调试插件 vue开发浏览器调试插件 vue开发浏览器调试插件 vue开发浏览器调试插件
  • Chrome浏览器vue开发插件,解决本地扩展程序中没有vue的情况,对前端页面进行高效调试
  • This extension demonstrates a browser action with kittens.
  • 之前找了不少如何开发谷歌插件的文章,结果发现都是些很基础的内容,并没有写到如何...后来发现了vue-cli-plugin-chrome-ext插件,通过这个插件能很方便地用vue-cli3来开发谷歌插件,并能直接引用各种UI框架跟npm插件

    转自: Mrli2016 的博客 vue-cli3开发Chrome Extension实践

    简介

    之前找了不少如何开发谷歌插件的文章,结果发现都是些很基础的内容,并没有写到如何快速编译打包插件。我就在想为什么不能通过webpack来打包插件呢?如果通过webpack编译的话,就能使开发过程变得更舒服,使文件结构趋向模块化,并且打包的时候直接编译压缩代码。后来发现了vue-cli-plugin-chrome-ext插件,通过这个插件能很方便地用vue-cli3来开发谷歌插件,并能直接引用各种UI框架跟npm插件。

    tip:如果你没接触过谷歌插件开发的话建议先看看基础文档:

    环境需求

    • node
    • yarn
    • vue cli
      注意:因开发环境随时间前进,存在版本优化升级,按本文操作可能存在个别微差,有能力者自行解决,如无法解决可在文末下载已搭建好的框架模板,或调整到和我一致的开发环境
      slongzhang - vue extension开发环境

    搭建环境

    1. 创建一个vue-cli3项目: vue create vue-extension,对话流程选择默认就行。
      slongzhang - 创建vue项目

    2. 进入项目cd vue-extension
      slongzhang - 进入vue项目

    3. 安装vue-cli-plugin-chrome-ext插件:vue add chrome-ext,根据安装对话选项设置好。
      slongzhang - 安装chrome-ext

    4. 删除vue-cli3无用文件跟文件夹:src/main.js、src/components
      slongzhang - 删除无用文件及文件夹

    运行项目

    • 运行开发环境
    npm run build-watch
    

    slongzhang - 开发环境运行

    运行开发环境,对修改文件进行实时编译并自动在根目录下生成dist文件夹,然后在浏览器上加载dist文件夹完成插件安装。(注意:修改background文件跟manifest.json文件并不能实时刷新代码,需要重新加载插件才行。 后面已经有实时刷新的解决方法)

    slongzhang - 浏览器插件重载代码

    • 运行生产环境
    npm run build
    

    运行生产环境编译打包,将所有文件进行整合打包。

    浏览器加载生成的dish

    slongzhang - 加载开发的插件

    引入element UI

    • 目前的插件加载到浏览器后弹出页面是这种界面: 平时我们肯定要引入好看的UI框架的,在这里我们可以引入element-ui

    slongzhang - 点击图标弹出popup

    npm install element-ui
    

    slongzhang - 安装element-ui
    如果出现"Not Found - Get … @vue%2fvue-loader-…"的情况多半是选择vue2开发但vue cli版本过高导致,解决办法可以调整vue cli版本或安装指定"core-js"版本号(如3.6.5)
    slongzhang - vue extension package.json

    • 考虑到插件打包后的文件大小,最后通过按需加载的方式来引入组件,按照element-ui官方的按需加载方法,要先安装babel-plugin-component插件:
    npm install babel-plugin-component -D
    

    slongzhang - 安装按需加载插件babel-plugin-component

    • 然后,将babel.config.js修改为
    module.exports = {
      presets: [
        '@vue/app'
      ],
      "plugins": [
        [
          "component",
          {
            "libraryName": "element-ui",
            "styleLibraryName": "theme-chalk"
          }
        ]
      ]
    }
    

    slongzhang - 修改babel.config.js

    • 接下来修改popup相关文件引入所需组件, src/popup/index.js内容
    import Vue from "vue";
    import AppComponent from "./App/App.vue";
    
    Vue.component("app-component", AppComponent);
    
    import {
      Card
    } from 'element-ui';
    
    Vue.use(Card);
    
    new Vue({
      el: "#app",
      render: createElement => {
        return createElement(AppComponent);
      }
    });
    
    • src/popup/App/App.vue 内容
    <template>
      <el-card class="box-card">
        <div
          slot="header"
          class="clearfix"
        >
          <span>卡片名称</span>
          <el-button
            style="float: right; padding: 3px 0"
            type="text"
          >操作按钮</el-button>
        </div>
        <div
          v-for="o in 4"
          :key="o"
          class="text item"
        >
          {{'列表内容 ' + o }}
        </div>
      </el-card>
    </template>
    
    <script>
    export default {
      name: 'app',
    }
    </script>
    
    <style>
    .box-card {
      width: 300px;
    }
    </style>
    
    • 渲染效果
      slongzhang - 引入elementUi的popup渲染效果
      当然,不仅仅是插件内部的页面,还可以将element-ui组件插入到content页面。

    content.js(注入web脚本)使用element-ui组件

    content.js主要作用于浏览网页,对打开的网页进行插入、修改DOM,对其进行操作交互。别觉得element-ui只能配合vue使用,其实就是一个前端框架,只要我们引入了就能使用,webpack会自动帮我们抽离出来编译打包。

    根据评论的朋友提示,可以通过Chrome插件的chrome.extension.getURLAPI来引入字体文件,解决element-ui无法引入相对路径的字体文件问题。

    • 首先我们创建src/content/index文件,在里面我们通过chrome.extension.getURLAPI来引入插件的字体文件,内容
    import {
        Message,
        MessageBox
    } from 'element-ui';
    
    // 通过Chrome插件的API加载字体文件
    (function insertElementIcons() {
        let elementIcons = document.createElement('style')
        elementIcons.type = 'text/css';
        elementIcons.textContent = `
            @font-face {
                font-family: "element-icons";
                src: url('${ window.chrome.extension.getURL("fonts/element-icons.woff")}') format('woff'),
                url('${ window.chrome.extension.getURL("fonts/element-icons.ttf ")}') format('truetype'); /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
            }
        `
        document.head.appendChild(elementIcons);
    })();
    
    MessageBox.alert('这是一段内容', '标题名称', {
        confirmButtonText: '确定',
        callback: action => {
            Message({
                type: 'info',
                message: `action: ${ action }`
            });
        }
    })
    
    content.js里调用了chrome API而在vue里并不知道chrome是全局变量,可执行npm install --save @types/chrome 或 在根目录添加 .eslintrc.js 进行全局变量声明

    slongzhang - content.js(注入web脚本)使用element-ui组件

    • vue.config.js增加content.js文件的打包配置,因为content.js(background.js同样可以只生成js文件)只有js文件,不用像app模式那样打包生成相应的html文件。这里我们还要对字体打包配置处理下,因为默认打包后的文件名是带有hash值的,在这里我们要去除掉,完整内容如下
    const CopyWebpackPlugin = require("copy-webpack-plugin");
    const path = require("path");
    
    // Generate pages object
    const pagesObj = {};
    
    const chromeName = ["popup", "options"];
    
    chromeName.forEach(name => {
      pagesObj[name] = {
        entry: `src/${name}/index.js`,
        template: "public/index.html",
        filename: `${name}.html`
      };
    });
    
    // 生成manifest文件
    const manifest =
      process.env.NODE_ENV === "production" ? {
        from: path.resolve("src/manifest.production.json"),
        to: `${path.resolve("dist")}/manifest.json`
      } : {
        from: path.resolve("src/manifest.development.json"),
        to: `${path.resolve("dist")}/manifest.json`
      };
    
    const plugins = [
      CopyWebpackPlugin([
        manifest
      ])
    ]
    
    module.exports = {
      pages: pagesObj,
      // // 生产环境是否生成 sourceMap 文件
      productionSourceMap: false,
    
      configureWebpack: {
        entry: {
          'content': './src/content/index.js'
        },
        output: {
          filename: 'js/[name].js'
        },
        plugins: plugins 
      },
      css: {
        extract: {
          filename: 'css/[name].css'
          // chunkFilename: 'css/[name].css'
        }
      },
      chainWebpack: config => {
        // 处理字体文件名,去除hash值
        const fontsRule = config.module.rule('fonts')
    
        // 清除已有的所有 loader。
        // 如果你不这样做,接下来的 loader 会附加在该规则现有的 loader 之后。
        fontsRule.uses.clear()
        fontsRule.test(/\.(woff2?|eot|ttf|otf)(\?.*)?$/i)
          .use('url')
          .loader('url-loader')
          .options({
            limit: 1000,
            name: 'fonts/[name].[ext]'
          })
      }
    };
    
    • 最后在manifest.development.json加载content.js文件,以及将字体文件添加到网页可加载资源字段web_accessible_resources里去
    {
        "manifest_version": 2,
        "name": "chrome-ext",
        "description": "chrome extension",
        "version": "0.0.1",
        "options_page": "options.html",
        "browser_action": {
            "default_popup": "popup.html"
        },
        "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
        "content_scripts": [{
            "matches": [
                "*://*.baidu.com/*"
            ],
            "css": [
                "css/content.css"
            ]
            ,"js": [
                "js/content.js"
            ],
            "run_at": "document_end"
        }],
        "web_accessible_resources": ["fonts/*"]
    }
    
    • 然后浏览器重新加载插件后打开https://www.baidu.com/网址后可看到:

    slongzhang - 注入到百度

    实时刷新插件

    之前写的时候发现单纯地通过vue-cli3更新代码并不能使插件的background.js、content.js也跟着更新,因为代码已经加载到浏览器了,浏览器并不会监听这些文件的变化。也是通过评论的朋友推荐,通过crx-hotreload可以完美实现实时刷新功能,而不用重新手动加载。代码还简单易用,在这里我们直接将代码复制到src/utils/hot-reload.js文件:

    // 代码来源:https://github.com/xpl/crx-hotreload/edit/master/hot-reload.js
    const filesInDirectory = dir => new Promise(resolve =>
        dir.createReader().readEntries(entries =>
            Promise.all(entries.filter(e => e.name[0] !== '.').map(e =>
                e.isDirectory ?
                filesInDirectory(e) :
                new Promise(resolve => e.file(resolve))
            ))
            .then(files => [].concat(...files))
            .then(resolve)
        )
    )
    
    const timestampForFilesInDirectory = dir =>
        filesInDirectory(dir).then(files =>
            files.map(f => f.name + f.lastModifiedDate).join())
    
    const reload = () => {
        window.chrome.tabs.query({
            active: true,
            currentWindow: true
        }, tabs => { // NB: see https://github.com/xpl/crx-hotreload/issues/5
            if (tabs[0]) {
                window.chrome.tabs.reload(tabs[0].id)
            }
            window.chrome.runtime.reload()
        })
    }
    
    const watchChanges = (dir, lastTimestamp) => {
        timestampForFilesInDirectory(dir).then(timestamp => {
            if (!lastTimestamp || (lastTimestamp === timestamp)) {
                setTimeout(() => watchChanges(dir, timestamp), 1000) // retry after 1s
            } else {
                reload()
            }
        })
    }
    
    window.chrome.management.getSelf(self => {
        if (self.installType === 'development') {
            window.chrome.runtime.getPackageDirectoryEntry(dir => watchChanges(dir))
        }
    })
    
    • 然后在vue.config.js对热刷新代码进行处理,如果是开发环境的话就将其复制到assets文件夹里面:
    // vue.config.js
    ...
    
    // 在这段下面添加
    const plugins = [
      CopyWebpackPlugin([
        manifest
      ])
    ]
    
    // 开发环境将热加载文件复制到dist文件夹
    if (process.env.NODE_ENV !== 'production') {
      plugins.push(
        CopyWebpackPlugin([{
          from: path.resolve("src/utils/hot-reload.js"),
          to: path.resolve("dist")
        }])
      )
    }
    
    ...
    
    
    • vue.config.js 配置如图
      slongzhang - 添加热加载

    • 最后只要在开发环境manifest.development.json里配置一下,将hot-reload.js加载到background运行环境中即可:

    "background": { "scripts": ["hot-reload.js"] }
    

    slongzhang - 添加后台脚本

    添加打包文件大小预览配置

    既然用了vue-cli3了,怎能不继续折腾呢,我们平时用webpack开发肯定离不开打包组件预览功能,才能分析哪些组件占用文件大,该有的功能一个都不能少😎。这么实用的功能,实现起来也无非就是添加几行代码的事:

    // vue.config.js
    
    module.export = {
      /* ... */
    
      chainWebpack: config => {
        // 查看打包组件大小情况
        if (process.env.npm_config_report) {
          // 在运行命令中添加 --report参数运行, 如:npm run build --report
          config
            .plugin('webpack-bundle-analyzer')
            .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
        }
      }
    }
    
    • 配置代码如图
      slongzhang - 添加打包文件大小预览配置

    • 就辣么简单,然后运行npm run build --report看看效果

    npm run build --report
    

    slongzhang - 添加打包文件大小预览配置 效果图

    自动打包zip

    做过Chrome插件的都知道,提交到谷歌插件市场的话需要打包为zip文件才行。如果每次我们都需要将编译文件打包成zip的话就太麻烦了,这种每次都要经历的重复流程当然是交给程序来处理啦。 想打包zip的话首先要安装zip-webpack-plugin插件到开发环境:

    npm install --save-dev zip-webpack-plugin
    
    • 然后添加打包代码到vue.config.js:

    slongzhang - 添加打包代码到vue.config.js

    • vue.config.js最终全文代码
    // vue.config.js
    const CopyWebpackPlugin = require("copy-webpack-plugin");
    const ZipPlugin = require('zip-webpack-plugin');
    const path = require("path");
    
    // Generate pages object
    const pagesObj = {};
    
    const chromeName = ["popup", "options"];
    
    chromeName.forEach(name => {
      pagesObj[name] = {
        entry: `src/${name}/index.js`,
        template: "public/index.html",
        filename: `${name}.html`
      };
    });
    
    // 生成manifest文件
    const manifest =
      process.env.NODE_ENV === "production" ? {
        from: path.resolve("src/manifest.production.json"),
        to: `${path.resolve("dist")}/manifest.json`
      } : {
        from: path.resolve("src/manifest.development.json"),
        to: `${path.resolve("dist")}/manifest.json`
      };
    
    const plugins = [
      CopyWebpackPlugin([
        manifest
      ])
    ]
    
    // 开发环境将热加载文件复制到dist文件夹
    if (process.env.NODE_ENV !== 'production') {
      plugins.push(
        CopyWebpackPlugin([{
          from: path.resolve("src/utils/hot-reload.js"),
          to: path.resolve("dist")
        }])
      )
    }
    
    
    // 生产环境打包dist为zip
    if (process.env.NODE_ENV === 'production') {
      plugins.push(
        new ZipPlugin({
          path: path.resolve("dist"),
          filename: 'dist.zip',
        })
      )
    }
    
    module.exports = {
      pages: pagesObj,
      // // 生产环境是否生成 sourceMap 文件
      productionSourceMap: false,
    
      configureWebpack: {
        entry: {
          'content': './src/content/index.js'
        },
        output: {
          filename: 'js/[name].js'
        },
        plugins: plugins
      },
      css: {
        extract: {
          filename: 'css/[name].css'
          // chunkFilename: 'css/[name].css'
        }
      },
      chainWebpack: config => {
        // 处理字体文件名,去除hash值
        const fontsRule = config.module.rule('fonts')
    
        // 清除已有的所有 loader。
        // 如果你不这样做,接下来的 loader 会附加在该规则现有的 loader 之后。
        fontsRule.uses.clear()
        fontsRule.test(/\.(woff2?|eot|ttf|otf)(\?.*)?$/i)
          .use('url')
          .loader('url-loader')
          .options({
            limit: 1000,
            name: 'fonts/[name].[ext]'
          });
    
    
        // 查看打包组件大小情况
        if (process.env.npm_config_report) {
          config
            .plugin('webpack-bundle-analyzer')
            .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
        }
    
      }
    };
    
    

    搞定收工!

    结语

    事实证明,vue-cli3很强大,vue相关的插件并不是不能应用于开发浏览器插件,element-ui也不仅限于vue的运用。只有你想不到,没有做不到的事😁。

    tip:如果你懒得从头开始搭建模板的话也可以从GitHub拉取 vue-extension-templatecsdn 下载

    展开全文
  • vue浏览器插件开发工具devtools,使用方式:打开谷歌扩展程序页面,并打开该页的开发者模式按钮,点击加载已解压的扩展程序,选择解压后的文件,重启浏览器,打开浏览器开发工具即可使用.
  • 使用vue开发chrome浏览器插件,可以方便前端人员开发插件页面,不管是popup页面,content页面等都可以使用vue开发模式很方便的开发需求 码云地址:https://gitee.com/guoqiankun/my-vue3-plugin github地址:...
  • 之前写过一篇 chrome 浏览器插件开发的文章 全方面手把手从0到1带你开发谷歌浏览器插件 ,但是不是 vue/react 这种第三方框架的,是原生和 jquery 混合的,但是那种开发前端方式比较麻烦,所以下面是用 vue开发...

    之前写过一篇 chrome 浏览器插件开发的文章 全方面手把手从0到1带你开发谷歌浏览器插件 ,但是不是 vue/react 这种第三方框架的,是原生和 jquery 混合的,但是那种开发前端方式比较麻烦,所以下面是用 vue 来开发插件

    github地址:https://github.com/18055975947/my-vue3-plugin
    码云地址:https://gitee.com/guoqiankun/my-vue3-plugin

    一、创建 Vue 项目

    使用 vue-cli 创建 vue3.x 版本的 vue 项目
    vue create my-vue3-plugin
    如果在创建项目的时候报错,报错内容如下:

    error Couldn't find package "postcss-normalize-string@^4.0.2" required by "cssnano-preset-default@^4.0.0" on the "npm" registry.
    Error: Couldn't find package "@vue/cli-overlay@^4.5.9" required by "@vue/cli-service@~4.5.0" on the "npm" registry.
        at MessageError.ExtendableBuiltin (/usr/local/lib/node_modules/yarn/lib/cli.js:243:66)
        at new MessageError (/usr/local/lib/node_modules/yarn/lib/cli.js:272:123)
        at PackageRequest.<anonymous> (/usr/local/lib/node_modules/yarn/lib/cli.js:38988:17)
        at Generator.throw (<anonymous>)
        at step (/usr/local/lib/node_modules/yarn/lib/cli.js:92:30)
        at /usr/local/lib/node_modules/yarn/lib/cli.js:105:13
        at process._tickCallback (internal/process/next_tick.js:68:7)
     ERROR  command failed: yarn 
    

    可以参考 使用 vue-cli 创建 vue3.x 版本项目报错 文章来处理
    此时文件目录为:

    .
    ├── README.md
    ├── babel.config.js
    ├── package.json
    ├── public
    │   ├── favicon.ico
    │   └── index.html
    ├── src
    │   ├── App.vue
    │   ├── assets
    │   │   └── logo.png
    │   ├── components
    │   │   └── HelloWorld.vue
    │   └── main.js
    └── yarn.lock
    

    二、修改项目

    因为我们要开发 chrome 插件项目,而这种生成的 vue 项目里面的文件夹和文件很多我们不需要,所以我们需要处理下:

    1. 在根目录下创建 vue.config.jsvue 配置文件;
    2. src 文件夹下面的 app.vue、components 文件夹删除
    3. assets 文件中创建 images 文件夹,并在 images 文件夹里面添加自己插件的 icon
    4. 删除根目录下的 public 文件夹
    5. src 文件夹下 创建 background、content、plugins、popup、utils 文件夹
    6. background 文件夹下创建 main.js
    7. content 文件夹下创建 components 文件夹和 main.jscomponents 文件夹下创建 app.vue
    8. plugins 文件夹下创建 inject.js、manifest.json 文件
    9. popup 文件夹下创建 components 文件夹 main.jsindex.htmlcomponents 文件夹下创建 app.vue

    步骤解析:

    • vue.config.jsvue 项目的打包、运行、等的配置文件,我们需要生成插件项目,这个文件需要创建并且自行配置
    • 删除多余的文件,我们插件里面目前只有 需要一个 popup 页面,不需要 外部的 app.vue 和 组件
    • 自己的插件 icon,按照 16 * 16、48 * 48、128 * 128 三个尺寸
    • 不需要 public 文件夹里面的 index.html
    • 创建我们插件需要的 background.js、content.js、popup页面、插件配置
    • 创建 background.js 文件
    • 创建 content.js 文件
    • 创建 popup.js、popup.html 文件

    此时文件目录:

    .
    ├── README.md
    ├── babel.config.js
    ├── package.json
    ├── src
    │   ├── assets
    │   │   ├── images
    │   │   │   ├── icon128.png
    │   │   │   ├── icon16.png
    │   │   │   └── icon48.png
    │   │   └── logo.png
    │   ├── background
    │   │   └── main.js
    │   ├── content
    │   │   ├── components
    │   │   │   └── app.vue
    │   │   └── main.js
    │   ├── main.js
    │   ├── plugins
    │   │   ├── inject.js
    │   │   └── manifest.json
    │   ├── popup
    │   │   ├── components
    │   │   │   └── app.vue
    │   │   ├── index.html
    │   │   └── main.js
    │   └── utils
    ├── vue.config.js
    └── yarn.lock
    

    三、配置项目

    1、plugins/manifest.json 文件配置

    先配置 manifest.json 文件,在按照此文件配置 vue.config.js 文件

    {
    	"manifest_version": 2,
    	"name": "my-vue3-plugin",
    	"description": "基于vue3.x版本的chrome插件",
    	"version": "1.0.0",
    	"browser_action": {
    		"default_title": "my-vue3-plugin",
    		"default_icon": "assets/images/icon48.png",
    		"default_popup": "popup.html"
    	},
    	"permissions": [],
    	"background": {
    		"scripts": ["js/background.js"]
    	},
    	"icons": {
    		"16": "assets/images/icon16.png",
    		"48": "assets/images/icon48.png",
    		"128": "assets/images/icon128.png"
    	},
    	"content_scripts": [
    		{
    			"matches": ["https://*.taobao.com/*"],
    			"css": ["css/content.css"],
    			"js": ["js/content.js"],
    			"run_at": "document_idle"
    		}
    	],
    	"web_accessible_resources": ["js/inject.js"]
    }
    

    解析:

    1. browser_action 中的 default_popup 配置为 和 manifest.json 文件一级的 popup.html
    2. browser_action 中的 default_icon 配置为 assets/images/icon48.png
    3. background 配置为 js/background.js
    4. icons 文件进行 项目的配置
    5. content_scripts 配置对应的 js、css、和 matches
    6. web_accessible_resources 配置网页内置 js/inject.js

    2、配置 vue.config.js 文件

    通过上面的 manifest.json 文件可以看出,我们需要配置 js 文件夹,css 文件夹,popup.html 文件,background.js 文件,inject.js 文件,content.js 文件,content.css 文件;

    1. 添加 copy-webpack-plugin 模块,用于复制文件

    我们需要把 plugins 文件夹下的文件复制到打包之后的 dist 文件中

    安装:

    yarn add copy-webpack-plugin@6.0.2 --dev
    

    2. 文件内容

    const CopyWebpackPlugin = require("copy-webpack-plugin");
    const path = require("path");
    
    // 复制文件到指定目录
    const copyFiles = [
    	{
        	from: path.resolve("src/plugins/manifest.json"),
        	to: `${path.resolve("dist")}/manifest.json`
      	},
      	{
        	from: path.resolve("src/assets"),
        	to: path.resolve("dist/assets")
      	},
      	{
    	    from: path.resolve("src/plugins/inject.js"),
    	    to: path.resolve("dist/js")
      	}
    ];
    
    // 复制插件
    const plugins = [
      	new CopyWebpackPlugin({
        	patterns: copyFiles
      	})
    ];
    
    // 页面文件
    const pages = {};
    // 配置 popup.html 页面
    const chromeName = ["popup"];
    
    chromeName.forEach(name => {
      	pages[name] = {
        	entry: `src/${name}/main.js`,
        	template: `src/${name}/index.html`,
        	filename: `${name}.html`
      	};
    });
    
    module.exports = {
    	pages,
    	productionSourceMap: false,
    	// 配置 content.js background.js
    	configureWebpack: {
    		entry: {
    			content: "./src/content/main.js",
    			background: "./src/background/main.js"
    		},
    		output: {
    			filename: "js/[name].js"
    		},
    		plugins
    	},
    	// 配置 content.css
    	css: {
    		extract: {
    			filename: "css/[name].css"
    		}
    	}
    }
    

    3. 解析:

    1. copyFiles 是复制文件的字段
    2. pages 是配置多页面的文件字段
    3. configureWebpack 来配置 content.jsbackground.js 文件
    4. css 配置 content.css 文件

    3、popup 文件夹修改

    从上面的配置我们知道了,popup 文件夹是用来生成 browser_actionpopup.html 文件的,所以此时我们来写入 popup 文件夹

    1. popup/index.html

    popup 文件夹下的 index.html 文件,因为这个是 html 文件,我们就只需要按照 vue create 生成的项目中的 public 文件夹下的 index.html 文件内容拷贝过来即可,顺便把 favicon 删除,把 title 修改下

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width,initial-scale=1.0">
        <title>my-vue-chrome-plugin</title>
      </head>
      <body>
        <noscript>
          <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
        </noscript>
        <div id="app"></div>
        <!-- built files will be auto injected -->
      </body>
    </html>
    

    2. popup/main.js

    这个是 vue 项目的入口配置文件,就按照 src 下面的 main.js 复制过来即可,别忘了改下 大小写

    import { createApp } from 'vue'
    import app from './components/app.vue'
    
    createApp(app).mount('#app')
    

    3. popup/components/app.vue

    此文件就是正常的 vue 文件,按照平时写 vue 项目开发即可

    <template>
    	<div class="popup_page">
    		this is popup page
    		<div class="popup_page_main">
    			this is popup page main
    		</div>
    	</div>
    </template>
    
    <script>
    	export default {
    
    	}
    </script>
    
    <style></style>
    

    4、content 文件夹修改

    content 文件夹下是对应 chrome 插件的 content.js,这个可以在嵌入页面里面渲染页面,我们也可以用 vue 开发

    1. content/components/app.vue

    正常的 vue 开发

    <template>
    	<div class="content_page">
    		content_page
    		<div class="content_page_main">
    			content_page_main
    		</div>
    	</div>
    </template>
    
    <script>
    	export default {
    
    	}
    </script>
    
    <style>
    	
    </style>
    

    2. content/main.js

    main.js 这个文件是比较重要的,是通过这个文件引入 vue 组件以及使用 vue 开发 content 页面的,所以这个页面,需要在插件嵌入的页面,增加一个 dom 元素,并把这个插件的 content 页面,渲染进去。

    import { createApp } from 'vue'
    import app from './components/app.vue'
    
    joinContent(app)
    
    function joinContent (element) {
    	const div = document.createElement('div')
    	div.id = 'joinContentApp'
    	document.body.appendChild(div)
    	console.log(div)
    	createApp(element).mount('#joinContentApp')
    }
    

    解析:

    1. 引入 vue3createApp
    2. 引入 app 组件
    3. 创建一个 idjoinContentAppdom 元素,把此元素插入 body 中,并把应用实例挂载到此 dom

    5、background 文件夹

    此文件夹是对应的 background.js 文件,可以只写一个简单的日志打印即可

    console.log('this is background main.js')
    

    6、yarn run build 打包

    此时,先进行 run build 打包,如果你报错了,是 eslint 报错,可以进行在 .eslintrc.js 文件中进行配置,添加一些我常用的 eslint 配置

    module.exports = {
      root: true,
      env: {
        node: true
      },
      extends: [
        'plugin:vue/vue3-essential',
        '@vue/standard'
      ],
      parserOptions: {
        parser: 'babel-eslint'
      },
      rules: {
        "generator-star-spacing": "off",
        "object-curly-spacing": "off",
        "no-var": "error",
        "semi": 0,
        "eol-last": "off",
        "no-tabs": "off",
        "indent": "off",
        "quote-props": 0,
        "no-mixed-spaces-and-tabs": "off",
        "no-trailing-spaces": "off",
        "arrow-parens": 0,
        "spaced-comment": "off",
        "space-before-function-paren": "off",
        "no-empty": "off",
        "no-else-return": "off",
        "no-unused-vars": [2, {"vars": "all", "args": "after-used"}],
        "no-console": "off",
        'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
      }
    }
    

    然后在进行打包

    此时的文件内容

    .
    ├── README.md
    ├── babel.config.js
    ├── dist
    │   ├── assets
    │   │   ├── images
    │   │   │   ├── icon128.png
    │   │   │   ├── icon16.png
    │   │   │   └── icon48.png
    │   │   └── logo.png
    │   ├── js
    │   │   ├── background.js
    │   │   ├── chunk-vendors.fa86ccee.js
    │   │   ├── content.js
    │   │   ├── inject.js
    │   │   └── popup.js
    │   ├── manifest.json
    │   └── popup.html
    ├── package.json
    ├── src
    │   ├── assets
    │   │   ├── images
    │   │   │   ├── icon128.png
    │   │   │   ├── icon16.png
    │   │   │   └── icon48.png
    │   │   └── logo.png
    │   ├── background
    │   │   └── main.js
    │   ├── content
    │   │   ├── components
    │   │   │   └── app.vue
    │   │   └── main.js
    │   ├── main.js
    │   ├── plugins
    │   │   ├── inject.js
    │   │   └── manifest.json
    │   ├── popup
    │   │   ├── components
    │   │   │   └── app.vue
    │   │   ├── index.html
    │   │   └── main.js
    │   └── utils
    ├── vue.config.js
    └── yarn.lock
    

    此时我们可以看到 dist 文件夹下已经按照我们需要的内容进行打包了,但是没有 css 文件夹那是因为我们没有写入 css

    7、引入 less

    我们写页面少不了使用 css,现在都是使用预处理器,我比较倾向于 less,所以我使用 less、less-loader

    1. 引入 less less-loader

    yarn add less less-loader --dev
    

    2. 修改 app.vue 文件

    然后我们在 content/components/app.vuepopup/components/app.vue 文件中写入 css 样式

    content/components/app.vue

    <template>
    	<div class="content_page">
    		content_page
    		<div class="content_page_main">
    			content_page_main
    		</div>
    	</div>
    </template>
    
    <script>
    	export default {
    
    	}
    </script>
    
    <style lang="less" scoped>
    	.content_page{
    		color: red;
    		position: fixed;
    		z-index: 100001;
    		right: 10px;
    		bottom: 10px;
    		.content_page_main{
    			color: green;
    		}
    	}
    </style>
    

    popup/components/app.vue

    <template>
    	<div class="popup_page">
    		this is popup page
    		<div class="popup_page_main">
    			this is popup page main
    		</div>
    	</div>
    </template>
    
    <script>
    	export default {
    
    	}
    </script>
    
    <style lang="less" scoped>
    	.popup_page{
    		color: red;
    		.popup_page_main{
    			color: green;
    		}
    	}
    </style>
    

    3. yarn run build 打包

    此时 tree dist 查看 dist 文件夹内容

    dist
    ├── assets
    │   ├── images
    │   │   ├── icon128.png
    │   │   ├── icon16.png
    │   │   └── icon48.png
    │   └── logo.png
    ├── css
    │   ├── content.css
    │   └── popup.css
    ├── js
    │   ├── background.js
    │   ├── chunk-vendors.4f73d0d4.js
    │   ├── content.js
    │   ├── inject.js
    │   └── popup.js
    ├── manifest.json
    └── popup.html
    

    我们可以看到,我们通过 vue.config.js 文件配置的内容都已经生成到 dist 文件夹中了

    四、导入插件项目

    1、在谷歌拓展程序中打开我们的插件

    点击 加载已解压的拓展程序,选择我们的 dist 文件夹,此时我们的插件就被引入进来了

    插件

    2、打开 淘宝首页

    1. 为什么打开淘宝首页呢?

    因为我们在 manifest.json 中的 content_scripts 中配置 "matches": ["https://*.taobao.com/*"]

    2. 点击右上角的插件 icon

    popup
    我们可以看到我们的 popup 页面已经我们给它写的样式

    3、我们的 content 文件呢?

    我们在 content 文件中配置的 main.jsapp.vue 也写入了样式,也挂载到 dom 实例上了,但是为什么没有渲染,也没有打印

    function joinContent (element) {
    	const div = document.createElement('div')
    	div.id = 'joinContentApp'
    	document.body.appendChild(div)
    	console.log(div)
    	createApp(element).mount('#joinContentApp')
    }
    

    我们在 js 文件中有个 console.log 日志输入,但是可以看到淘宝页面的控制台并没有输入

    1. 为什么没有输出

    因为我们是用的 vue 开发项目,在 main.js 中是用的 vue 开发,所以我们得引入 vue 文件,得在 content 中引入 vue 才可以

    2. 解决方法,引入 vue

    我们可以看到 dist 文件夹下面有一个 chunk-vendors.4f73d0d4.js,这个就是 vue 打包之后的文件,我们先在 dist 中的 manifest.json 文件先把它引入进来先看下

    dist/manifest.json 文件下的 content_scripts 字段

    "content_scripts": [
    	{
    		"matches": ["https://*.taobao.com/*"],
    		"css": ["css/content.css"],
    		"js": ["js/chunk-vendors.4f73d0d4.js", "js/content.js"],
    		"run_at": "document_idle"
    	}
    ],
    

    此时,拓展程序页面刷新插件,并刷新淘宝首页,可以看到

    控制台输出
    content
    此时可以看到我们的 content 文件已经输出了。

    4、background.js 文件

    还记得我们在 background 文件夹下中的 main.js 写入日志输出吗?

    console.log('this is background main.js')
    

    我们打开拓展程序,找到我们的插件,点击 背景页 按钮

    在这里插入图片描述
    此时,背景页的控制台就出来,我们可以看到我们的日志输出,好像并没有输出我们的日志???

    1. 原因

    此问题的原因和上面的 content 文件的原因是一致的,也是没有引入 vue 文件

    2. 解决,引入 vue

    dist/manifest.jsonbackground 字段

    "background": {
    	"scripts": ["js/chunk-vendors.4f73d0d4.js", "js/background.js"]
    },
    

    此时,刷新插件,可以看到日志输出
    background日志输出

    5、引入 inject 文件

    1. 首先我们在 plugins/inject.js 文件中输出日志

    console.log('this is my inject.js')
    

    2. 然后在 content/main.js 文件中引入 inject.js

    import { createApp } from 'vue'
    import app from './components/app.vue'
    
    joinContent(app)
    injectJsInsert()
    
    function joinContent (element) {
    	const div = document.createElement('div')
    	div.id = 'joinContentApp'
    	document.body.appendChild(div)
    	console.log(div)
    	createApp(element).mount('#joinContentApp')
    }
    
    function injectJsInsert () {
    	document.addEventListener('readystatechange', () => {
    		const injectPath = 'js/inject.js'
    		const script = document.createElement('script')
    
    		script.setAttribute('type', 'text/javascript')
    		script.src = chrome.extension.getURL(injectPath)
    		document.body.appendChild(script)
    	})
    }
    

    3. yarn run build 打包

    此时打包可以发现报错了

    yarn run v1.22.10
    $ vue-cli-service build
    
    ⠼  Building for production...
    
     ERROR  Failed to compile with 1 error                              上午11:12:48
    
     error  in ./src/content/main.js
    
    Module Error (from ./node_modules/thread-loader/dist/cjs.js):
    
    /Users/guoqiankun/work/chromePlugin/my-vue3-plugin/src/content/main.js
      21:16  error  'chrome' is not defined  no-undef
    
    ✖ 1 problem (1 error, 0 warnings)
    
    
     ERROR  Build failed with errors.
    error Command failed with exit code 1.
    info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
    

    error 'chrome' is not defined no-undef

    我们在上面插入 jnject 文件使用的 chrome 未定义,那我们就定义一下;

    4. 修改 .eslintrc.js 文件

    root: true,
    globals: {
      chrome: true,
    },
    env: {
      node: true
    },
    

    增加一个 globals 字段,里面 chrome: true

    然后在进行 yarn run build 打包

    然后我们可以看到 dist/manifest.json 文件中的 content_scriptsbackground/scripts 已经没有引入 vue 了,所以我们不能在 dist 文件夹中修改,我们要在 plugins/manifest.json 文件中修改

    但是我们可以看到,我们每次打包生成的 chunk-vendors.js 会跟一个 hash,因为我们此时没有修改别的文件,所以 hash 后缀没有变化,但是如果我们改了内容之后在 yarn run build 呢?此时 hash 就会变化,总不能在改一次 manifest.json 再打一次包吧…

    6、修改 vue.config.js 文件,让打包时生成的 chunk-vendors.js 不带 hash

    1. 配置 chainWebpack 字段

    配置 chainWebpack 字段,对 config 内容进行处理

    module.exports = {
    	pages,
    	productionSourceMap: false,
    	// 配置 content.js background.js
    	configureWebpack: {
    		entry: {
    			content: "./src/content/main.js",
    			background: "./src/background/main.js"
    		},
    		output: {
    			filename: "js/[name].js"
    		},
    		plugins
    	},
    	// 配置 content.css
    	css: {
    		extract: {
    			filename: "css/[name].css"
    		}
    	},
    	chainWebpack: config => {
    		if (process.env.NODE_ENV === 'production') {
    			config.output.filename('js/[name].js').end()
    			config.output.chunkFilename('js/[name].js').end()
    		}
    	}
    }
    

    2. 修改 plugin/manifest.json 文件

    在此文件中引入 chunk-vendors.js

    plugin/manifest.json

    "background": {
    	"scripts": ["js/chunk-vendors.js", "js/background.js"]
    },
    "icons": {
    	"16": "assets/images/icon16.png",
    	"48": "assets/images/icon48.png",
    	"128": "assets/images/icon128.png"
    },
    "content_scripts": [
    	{
    		"matches": ["https://*.taobao.com/*"],
    		"css": ["css/content.css"],
    		"js": ["js/chunk-vendors.js", "js/content.js"],
    		"run_at": "document_idle"
    	}
    ],
    

    3. yarn run build 打包

    dist
    ├── assets
    │   ├── images
    │   │   ├── icon128.png
    │   │   ├── icon16.png
    │   │   └── icon48.png
    │   └── logo.png
    ├── css
    │   ├── content.css
    │   └── popup.css
    ├── js
    │   ├── background.js
    │   ├── chunk-vendors.js
    │   ├── content.js
    │   ├── inject.js
    │   └── popup.js
    ├── manifest.json
    └── popup.html
    

    4. 刷新插件,刷新页面

    属性插件,刷新页面,之后可以看到
    console
    inject.js

    五、热加载

    此时我们的 vue 开发插件项目已经基本上可以了,剩下的就是按照需求开发插件页面,按照需求添加 manifest.json 字段即可,但是我们不能每一次想看样式就打个包,然后属性插件,刷新页面看下,这样也可以,但是我们是开发,这样效率比较低,我不服…

    所以我们需要添加一下热加载

    1、热加载

    utils 文件夹下创建 hotReload.js 文件

    写入

    // 加载文件
    
    const filesInDirectory = dir =>
      new Promise(resolve =>
        dir.createReader().readEntries(entries => {
          Promise.all(
              entries
              .filter(e => e.name[0] !== '.')
              .map(e =>
                e.isDirectory ? filesInDirectory(e) : new Promise(resolve => e.file(resolve))
              )
            )
            .then(files => [].concat(...files))
            .then(resolve);
        })
      );
    
    // 遍历插件目录,读取文件信息,组合文件名称和修改时间成数据
    const timestampForFilesInDirectory = dir =>
      filesInDirectory(dir).then(files =>
        files.map(f => f.name + f.lastModifiedDate).join()
      );
    
    // 刷新当前活动页
    const reload = () => {
      window.chrome.tabs.query({
          active: true,
          currentWindow: true
        },
        tabs => {
          // NB: see https://github.com/xpl/crx-hotreload/issues/5
          if (tabs[0]) {
            window.chrome.tabs.reload(tabs[0].id);
          }
          // 强制刷新页面
          window.chrome.runtime.reload();
        }
      );
    };
    
    // 观察文件改动
    const watchChanges = (dir, lastTimestamp) => {
      timestampForFilesInDirectory(dir).then(timestamp => {
        // 文件没有改动则循环监听watchChanges方法
        if (!lastTimestamp || lastTimestamp === timestamp) {
          setTimeout(() => watchChanges(dir, timestamp), 1000); // retry after 1s
        } else {
          // 强制刷新页面
          reload();
        }
      });
    };
    
    const hotReload = () => {
      window.chrome.management.getSelf(self => {
        if (self.installType === 'development') {
          // 获取插件目录,监听文件变化
          window.chrome.runtime.getPackageDirectoryEntry(dir => watchChanges(dir));
        }
      });
    };
    
    export default hotReload;
    

    2、引入

    bckground/main.js 中 引入

    import hotReload from '@/utils/hotReload'
    
    hotReload()
    console.log('this is background main.js')
    

    3、修改 package.json 中的 scripts

    1. 增加一个 watch 用来监听打包

    "scripts": {
      "watch": "vue-cli-service build --watch",
      "serve": "vue-cli-service serve",
      "build": "vue-cli-service build",
      "lint": "vue-cli-service lint"
    },
    

    2. 此时运行 yarn run watch

    yarn run v1.22.10
    $ vue-cli-service build --watch
    
    ⠙  Building for development...
    
     DONE  Compiled successfully in 3956ms                                           上午11:39:00
    
      File                        Size                          Gzipped
    
      dist/js/chunk-vendors.js    668.79 KiB                    122.48 KiB
      dist/js/content.js          26.47 KiB                     3.71 KiB
      dist/js/popup.js            26.23 KiB                     3.55 KiB
      dist/js/background.js       15.57 KiB                     3.30 KiB
      dist/js/inject.js           0.04 KiB                      0.05 KiB
      dist/css/content.css        0.18 KiB                      0.14 KiB
      dist/css/popup.css          0.11 KiB                      0.09 KiB
    
      Images and other types of assets omitted.
    
     DONE  Build complete. Watching for changes...
    

    可以看到一直在监听改变

    3. 然后我们刷新插件和页面

    发现有一个报错

    Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' blob: filesystem:".
    

    报错

    4. 按照错误解决问题:

    plugins/manifest.json 中添加:

    "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
    

    5. 重新运行 yarn run bild

    6. 清除插件错误、刷新插件、淘宝页面

    4、修改 content/components/app.vue 文件

    <template>
    	<div class="content_page">
    		content_page
    		<div class="content_page_main">
    			content_page_main
    		</div>
    		<div class="content_page_footer">
    			content_page_footer
    		</div>
    	</div>
    </template>
    

    保存

    然后发现插件自动刷新、浏览器页面自动刷新。

    此时浏览器页面右下角我们新加的内容就展示在上面了。
    在这里插入图片描述

    六、总结

    1. 使用 vue 开发插件,要先想一下我们要做成什么样
    2. 针对性的修改对应的文件,按照我们的需求去配置
    3. 遇到问题先想一下哪一步的问题,为什么出现,可以自己先想想,最后在寻求帮助
    4. 完结🎉🎉🎉

    七、源码地址

    github地址:https://github.com/18055975947/my-vue3-plugin

    码云地址:https://gitee.com/guoqiankun/my-vue3-plugin

    参考资料
    展开全文
  • 根据最新vue插件源码编译,支持最新chrome浏览器,chrome开发者模式选择解压后的目录安装插件
  • 要开启评估平台,查看平台和保存平台,感觉非常繁琐,开发了一款可以获取评估平台数据,查看项目排期和直接保存数据到数据库的chrome插件,由于我需要使用之前vue封装的一个日历插件,这里就用vue开发这个插件。...
  • 使用Vue开发Chrome插件

    千次阅读 2021-09-18 12:41:45
    原文链接:使用Vue开发Chrome插件 - 愧怍的小站 (kuizuo.cn) 前言 我当时学习开发Chrome插件的时候,还不会Vue,更别说Webpack了,所以使用的都是原生的html开发,效率就不提了,而这次就准备使用vue-cli来进行编写...

    原文链接: 使用Vue开发Chrome插件 - 愧怍的小站 (kuizuo.cn)

    前言

    我当时学习开发Chrome插件的时候,还不会Vue,更别说Webpack了,所以使用的都是原生的html开发,效率就不提了,而这次就准备使用vue-cli来进行编写一个某B站获取视频信息,评论的功能(原本是打算做自动回复的),顺便巩固下chrome开发(快一年没碰脚本类相关技术了),顺便写套模板供自己后续编写Chrome插件做铺垫。

    关于Chrome插件开发的基本知识就不赘述了,之前写过一篇原生开发的Chrome插件开发 - 愧怍的小站,里面有附带相关文档链接。

    相关代码开源github地址

    环境搭建

    Vue Web-Extension - A Web-Extension preset for VueJS (vue-web-extension.netlify.app)

    npm install -g @vue/cli
    npm install -g @vue/cli-init
    vue create --preset kocal/vue-web-extension my-extension
    cd my-extension
    npm run server
    复制代码

    会提供几个选项,如Eslint,background.js,tab页,axios,如下图

    image-20210916142751129

    选择完后,将会自动下载依赖,通过npm run server将会在根目录生成dist文件夹,将该文件拖至Chrome插件管理便可安装,由于使用了webpack,所以更改代码将会热更新,不用反复的编译导入。

    项目结构

    ├─src
    |  ├─App.vue
    |  ├─background.js
    |  ├─main.js
    |  ├─manifest.json
    |  ├─views
    |  |   ├─About.vue
    |  |   └Home.vue
    |  ├─store
    |  |   └index.js
    |  ├─standalone
    |  |     ├─App.vue
    |  |     └main.js
    |  ├─router
    |  |   └index.js
    |  ├─popup
    |  |   ├─App.vue
    |  |   └main.js
    |  ├─override
    |  |    ├─App.vue
    |  |    └main.js
    |  ├─options
    |  |    ├─App.vue
    |  |    └main.js
    |  ├─devtools
    |  |    ├─App.vue
    |  |    └main.js
    |  ├─content-scripts
    |  |        └content-script.js
    |  ├─components
    |  |     └HelloWorld.vue
    |  ├─assets
    |  |   └logo.png
    ├─public
    ├─.browserslistrc
    ├─.eslintrc.js
    ├─.gitignore
    ├─babel.config.js
    ├─package.json
    ├─vue.config.js
    ├─yarn.lock
    复制代码

    根据所选的页面,并在src与vue.config.js中配置页面信息编译后dist目录结构如下

    ├─devtools.html
    ├─favicon.ico
    ├─index.html
    ├─manifest.json
    ├─options.html
    ├─override.html
    ├─popup.html
    ├─_locales
    ├─js
    ├─icons
    ├─css
    复制代码

    安装组件库

    安装elementUI

    整体的开发和vue2开发基本上没太大的区别,不过既然是用vue来开发的话,那肯定少不了组件库了。

    要导入Element-ui也十分简单,Vue.use(ElementUI); Vue2中怎么导入element,便怎么导入。演示如下

    image-20210916150154078

    不过我没有使用babel-plugin-component来按需引入,按需引入一个按钮打包后大约1.6m,而全量引入则是5.5左右。至于为什么不用,因为我需要在content-scripts.js中引入element组件,如果使用babel-plugin-component将无法按需导入组件以及样式(应该是只支持vue文件按需引入,总之就是折腾了我一个晚上的时间)

    安装tailwindcss

    不过官方提供了如何使用TailwindCSS,这里就演示一下

    在 Vue 3 和 Vite 安装 Tailwind CSS - Tailwind CSS 中文文档

    推荐安装低版本,最新版有兼容性问题

    npm install tailwindcss@npm:@tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9
    复制代码

    创建postcss.config.js文件

    // postcss.config.js
    module.exports = {
      plugins: [
        // ...
        require('tailwindcss'),
        require('autoprefixer'), // if you have installed `autoprefixer`
        // ...
      ]
    }
    复制代码

    创建tailwind.config.js文件

    // tailwind.config.js
    module.exports = {
      purge: {
        // Specify the paths to all of the template files in your project
        content: ['src/**/*.vue'],
      
        // Whitelist selectors by using regular expression
        whitelistPatterns: [
            /-(leave|enter|appear)(|-(to|from|active))$/, // transitions
            /data-v-.*/, // scoped css
        ],
      }
      // ...
    }
    复制代码

    在src/popup/App.vue中导入样式,或在新建style.css在mian.js中import "../style.css";

    <style>
    /* purgecss start ignore */
    @tailwind base;
    @tailwind components;
    /* purgecss end ignore */
    
    @tailwind utilities;
    </style>
    复制代码

    从官方例子导入一个登陆表单,效果如下

    image-20210916152633247

    项目搭建

    页面搭建

    页面搭建就没什么好说的了,因为使用的是element-ui,所以页面很快就搭建完毕了,效果如图

    image-20210918115438700

    悬浮窗

    悬浮窗其实可有可无,不过之前写Chrome插件的时候就写了悬浮窗,所以vue版的也顺带写一份。

    要注意的是悬浮窗是内嵌到网页的(且在document加载前载入,也就是"run_at": "document_start"),所以需要通过content-scripts.js才能操作页面Dom元素,首先在配置清单manifest.json与vue.confing.js中匹配要添加的网站,以及注入的js代码,如下

      "content_scripts": [
        {
          "matches": ["https://www.bilibili.com/video/*"],
          "js": ["js/jquery.js", "js/content-script.js"],
          "css": ["css/index.css"],
          "run_at": "document_start"
        },
        {
          "matches": ["https://www.bilibili.com/video/*"],
          "js": ["js/jquery.js", "js/bilibili.js"],
          "run_at": "document_end"
        }
      ]
    复制代码
    	contentScripts: {
              entries: {
                'content-script': ['src/content-scripts/content-script.js'],
                bilibili: ['src/content-scripts/bilibili.js'],
              },
            },
    复制代码

    由于是用Vue,但又要在js中生成组件,就使用document.createElement来进行创建元素,Vue组件如下(可拖拽)

    image-20210917142340863

    :::danger

    如果使用babel-plugin-component按需引入,组件的样式将无法载入,同时自定义组件如果编写了style标签,那么也同样无法载入,报错:Cannot read properties of undefined (reading 'appendChild')

    大致就是css-loader无法加载对应的css代码,如果执意要写css的话,直接在manifest.json中注入css即可

    :::

    完整代码

    // 注意,这里引入的vue是运行时的模块,因为content是插入到目标页面,对组件的渲染需要运行时的vue, 而不是编译环境的vue (我也不知道我在说啥,反正大概意思就是这样)
    import Vue from 'vue/dist/vue.esm.js';
    import ElementUI, { Message } from 'element-ui';
    Vue.use(ElementUI);
    
    // 注意,必须设置了run_at=document_start此段代码才会生效
    document.addEventListener('DOMContentLoaded', function() {
      console.log('vue-chrome扩展已载入');
    
      insertFloat();
    });
    
    // 在target页面中新建一个带有id的dom元素,将vue对象挂载到这个dom上。
    function insertFloat() {
      let element = document.createElement('div');
      let attr = document.createAttribute('id');
      attr.value = 'appPlugin';
      element.setAttributeNode(attr);
      document.getElementsByTagName('body')[0].appendChild(element);
    
      let link = document.createElement('link');
      let linkAttr = document.createAttribute('rel');
      linkAttr.value = 'stylesheet';
      let linkHref = document.createAttribute('href');
      linkHref.value = 'https://unpkg.com/element-ui/lib/theme-chalk/index.css';
      link.setAttributeNode(linkAttr);
      link.setAttributeNode(linkHref);
      document.getElementsByTagName('head')[0].appendChild(link);
    
      let left = 0;
      let top = 0;
      let mx = 0;
      let my = 0;
      let onDrag = false;
    
      var drag = {
        inserted: function(el) {
          (el.onmousedown = function(e) {
            left = el.offsetLeft;
            top = el.offsetTop;
            mx = e.clientX;
            my = e.clientY;
            if (my - top > 40) return;
    
            onDrag = true;
          }),
            (window.onmousemove = function(e) {
              if (onDrag) {
                let nx = e.clientX - mx + left;
                let ny = e.clientY - my + top;
                let width = el.clientWidth;
                let height = el.clientHeight;
                let bodyWidth = window.document.body.clientWidth;
                let bodyHeight = window.document.body.clientHeight;
    
                if (nx < 0) nx = 0;
                if (ny < 0) ny = 0;
    
                if (ny > bodyHeight - height && bodyHeight - height > 0) {
                  ny = bodyHeight - height;
                }
    
                if (nx > bodyWidth - width) {
                  nx = bodyWidth - width;
                }
    
                el.style.left = nx + 'px';
                el.style.top = ny + 'px';
              }
            }),
            (el.onmouseup = function(e) {
              if (onDrag) {
                onDrag = false;
              }
            });
        },
      };
    
      window.kz_vm = new Vue({
        el: '#appPlugin',
        directives: {
          drag: drag,
        },
        template: `
          <div class="float-page" ref="float" v-drag>
            <el-card class="box-card" :body-style="{ padding: '15px' }">
              <div slot="header" class="clearfix" style="cursor: move">
                <span>悬浮窗</span>
                <el-button style="float: right; padding: 3px 0" type="text" @click="toggle">{{ show ? '收起' : '展开'}}</el-button>
              </div>
              <transition name="ul">
                <div v-if="show" class="ul-box">
                  <span> {{user}} </span>
                </div>
              </transition>
            </el-card>
          </div>
          `,
        data: function() {
          return {
            show: true,
            list: [],
            user: {
              username: '',
              follow: 0,
              title: '',
              view: 0,
            },
          };
        },
        mounted() {},
        methods: {
          toggle() {
            this.show = !this.show;
          },
        },
      });
    }
    
    复制代码

    因为只能在js中编写vue组件,所以得用template模板,同时使用了directives,给组件添加了拖拽的功能(尤其是window.onmousemove,如果是元素绑定他自身的鼠标移动事件,那么拖拽鼠标将会十分卡顿),还使用了transition来进行缓慢动画效果其中注入的css代码如下

    .float-page {
      width: 400px;
      border-radius: 8px;
      position: fixed;
      left: 50%;
      top: 25%;
      z-index: 1000001;
    }
    
    .el-card__header {
      padding: 10px 15px !important
    }
    
    .ul-box {
      height: 200px;
      overflow: hidden;
    }
    
    .ul-enter-active,
    .ul-leave-active {
      transition: all 0.5s;
    }
    .ul-enter,
    .ul-leave-to {
      height: 0;
    }
    复制代码

    相关逻辑可自行观看,这里不在赘述了,并不复杂。

    也顺带是复习一下HTML中鼠标事件和vue自定义命令了

    功能实现

    主要功能

    • 检测视频页面,输出对应up主,关注数以及视频标题播放(参数过多就不一一显示了)

    • 监控关键词根据内容判断是否点赞,例如文本出现了下次一定,那么就点赞。

    输出相关信息

    这个其实只要接触过一丢丢爬虫的肯定都会知道如何实现,通过右键审查元素,像这样

    image-20210918104907148

    然后使用dom操作,选择对应的元素,输出便可

    > document.querySelector("#v_upinfo > div.up-info_right > div.name > a.username").innerText
    < '老番茄'
    复制代码

    当然使用JQuery效果也是一样的。后续我都会使用JQuery来进行操作

    在src/content-script/bilibili.js中写下如下代码

    window.onload = function() {
      console.log('加载完毕');
    
      function getInfo() {
        let username = $('#v_upinfo > div.up-info_right > div.name > a.username').text();
        let follow = $(`#v_upinfo > div.up-info_right > div.btn-panel > div.default-btn.follow-btn.btn-transition.b-gz.following > span > span > span`).text();
        let title = $(`#viewbox_report > h1 > span`).text();
        let view = $('#viewbox_report > div > span.view').attr('title');
    
        console.log(username, follow, title, view);
      }
      
      getInfo();
    };
    复制代码

    重新加载插件,然后输出查看结果

    加载完毕
    bilibili.js:19 老番茄 1606.0万 顶级画质 总播放数2368406
    复制代码

    这些数据肯定单纯的输出肯定是没什么作用的,要能显示到内嵌悬浮窗口,或者是popup页面上(甚至发送ajax请求到远程服务器上保存)

    对上面代码微改一下

    window.onload = function() {
      console.log('加载完毕');
    
      function getInfo() {
        let username = $('#v_upinfo > div.up-info_right > div.name > a.username').text().trim()
        let follow = $(`#v_upinfo > div.up-info_right > div.btn-panel > div.default-btn.follow-btn.btn-transition.b-gz.following > span > span > span`).text();
        let title = $(`#viewbox_report > h1 > span`).text();
        let view = $('#viewbox_report > div > span.view').attr('title');
    
        //console.log(username, follow, title, view);
        window.kz_vm.user = {
          username,
          follow,
          title,
          view,
        };
    
      }
      getInfo();
    };
    复制代码

    其中window.kz_vm是通过window.kz_vm = new Vue() 初始化的,方便我们操作vm对象,就需要通过jquery选择元素在添加属性了。如果你想的话也可以直接在content-script.js上编写代码,这样就无需使用window对象,但这样导致一些业务逻辑都堆在一个文件里,所以我习惯分成bilibili.js 然后注入方式为document_end,然后在操作dom元素吗,实现效果如下

    image-20210918110958104

    如果像显示到popup页面只需要通过页面通信就行了,不过前提得先popup打开才行,所以一般都是通过background来进行中转,一般来说很少 content –> popup(因为操作popup的前提都是popup要打开),相对更多的是content –> background 或 popup –> content

    content-script主动发消息给后台 我是小茗同学 - 博客园 (cnblogs.com)

    实现评论

    这边简单编写了一下页面,通过popup给content,让content输入评论内容,与点击发送,先看效果

    bilibili_comment

    同样的,找到对应元素位置

    // 评论文本框
    $('#comment > div > div.comment > div > div.comment-send > div.textarea-container > textarea').val("要回复的内容");
    // 评论按钮
    $('#comment > div > div.comment > div > div.comment-send > div.textarea-container > button').click();
    复制代码

    接着就是写页面通信的了,可以看到是popup向content发送请求

    window.onload = function() {
      console.log('content加载完毕');
    
      function comment() {
        chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
          let { cmd, message } = request;
          if (cmd === 'addComment') {
            $('#comment > div > div.comment > div > div.comment-send > div.textarea-container > textarea').val(message);
            $('#comment > div > div.comment > div > div.comment-send > div.textarea-container > button').click();
          }
      
          sendResponse('我收到了你的消息!');
        });
      }
      
      comment();
    };
    复制代码
    <template>
      <div>
        <el-container>
          <el-header height="24">B站小工具</el-header>
          <el-main>
            <el-row :gutter="5">
              <el-input
                type="textarea"
                :rows="2"
                placeholder="请输入内容"
                v-model="message"
                class="mb-5"
              >
              </el-input>
    
              <div>
                <el-button @click="addComment">评论</el-button>
              </div>
            </el-row>
          </el-main>
        </el-container>
      </div>
    </template>
    
    <script>
    export default {
      name: 'App',
      data() {
        return {
          message: '',
          list: [],
          open: false,
        }
      },
      created() {
        chrome.storage.sync.get('list', (obj) => {
          this.list = obj['list']
        })
      },
      mounted() {
        chrome.runtime.onMessage.addListener(function (
          request,
          sender,
          sendResponse
        ) {
          console.log('收到来自content-script的消息:')
          console.log(request, sender, sendResponse)
          sendResponse('我是后台,我已收到你的消息:' + JSON.stringify(request))
        })
      },
      methods: {
        sendMessageToContentScript(message, callback) {
          chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
            chrome.tabs.sendMessage(tabs[0].id, message, function (response) {
              if (callback) callback(response)
            })
          })
        },
        addComment() {
          this.sendMessageToContentScript(
            { cmd: 'addComment', message: this.message },
            function () {
              console.log('来自content的回复:' + response)
            }
          )
        },
      },
    }
    </script>
    复制代码

    代码就不解读了,调用sendMessageToContentScript方法即可。相关源码可自行下载查看

    实现类似点赞功能也是同理的。

    整体体验

    当时写Chrome插件的效率不能说慢,反正不快就是了,像一些tips,都得自行封装。用过Vue的都知道写网页很方便,写Chrome插件未尝不是编写一个网页,当时的我在接触了Vue后就萌发了使用vue来编写Chrome的想法,当然肯定不止我一个这么想过,所以我在github上就能搜索到相应的源码,于是就有了这篇文章。

    如果有涉及到爬取数据相关的,我肯定是首选使用HTTP协议,如果在搞不定我会选择使用puppeteerjs,不过Chrome插件主要还是增强页面功能的,可以实现原本页面不具备的功能。

    本文仅仅只是初步体验,简单编写了个小项目,后期有可能会实现一个百度网盘一键填写提取码,Js自吐Hooke相关的。(原本是打算做pdd商家自动回复的,客户说要用客户端而不是网页端(客户端可以多号登陆),无奈,这篇博客就拿B站来演示了)

    展开全文
  • VueDevTools-5.3.3,Chrom浏览器Vue调试插件目前最新版本,可再开发这工具中进行vue的前端代码调试工作 Chrome and Firefox DevTools extension for debugging Vue.js applications.
  • 基于Vue.js 2.9.6搭建,引入多个插件,实现快速进入项目开发状态,多个用法在项目中都有示例。 引入的插件 Vuex 新建store/store.js,有简单的state、mutations、actions、getters示例 Axios 在utils/http.js里进行...
  • 浏览器调试vue2工具.zip
  • vite 是由 vue 作者尤雨溪开发的一种最新的脚手架工具,相比我们熟悉的 webpack 它有一下几个特点: 1、快速的冷启动 2、即时的模块热更新 3、真正的按需编译 Vite,一个基于浏览器原生 ES imports 的开发服务器。...
  • vue浏览器调试工具

    2018-07-03 11:59:28
    一款非常好用的vue浏览器调试工具 开发vue使用的插件 浏览器调试工具
  • 解决方法: 官方建议在项目入口文件(main.js)引入: Vue.config.devtools = true;
  • 原文 项目中需要从百度图片和谷歌图片批量抓取一系列关键词的图片,而且需要是大图资源,不能是缩略图。...于是我想起了以前玩过的杂技——浏览器插件。通过javascript控制浏览器打开网页,搜索关键词,页面
  • 1.插件安装不必多说 一定要用Vue.js 开发Vue.min.js 在控制面板就不会显示 2.本地调试 用的是file://协议修改插件允许访问文件网址 打上对勾 ...
  • Vue.js的Chrome浏览器开发插件DevTools的源码版本插件的安装步骤和插件安装包(.crx文件)的安装步骤。
  • 热门 高效开发 Chrome插件插件安装教程】 请下载文件后先解压,然后进入页面: chrome://extensions/ 将文件拖拽到该页面,完成安装。 具体步骤: https://t.csdnimg.cn/NxMv 【热门插件】 ·CSDN 浏览器...
  • Vue.js devtools:调试vue.js应用的浏览器扩展 Chrome插件
  • 热门 高效开发 Chrome插件插件安装教程】 请下载文件后先解压,然后进入页面: chrome://extensions/ 将文件拖拽到该页面,完成安装。 具体步骤: https://t.csdnimg.cn/NxMv 【热门插件】 ·CSDN 浏览器...
  • 1 下载插件 百度网盘:https://pan.baidu.com/s/1nuD48dlWYd-ERAJ2aiPIsw 提取密码:gaha 解压 2 打开谷歌浏览器 输入网址: chrome://extensions/ 或点击右上角的更所工具—拓展程序 打开右上角的开发者选项 3...
  • 前言一个 Chrome 插件,核心就是 manifest.json 文件,manifest.json 下的属性content_scripts 指定inject的脚本列表 js,css    【 injected】browser_action 中 default_popup指定popup...
  • vue浏览器本地调试扩展
  • x-browser-update一个Vue.js浏览器更新插件。 示例#安装依赖项npm install#在本地主机上热加载服务:8080 npm run dev美国x-browser-update一个Vue.js浏览器更新插件。 示例#安装依赖项npm install#可以在本地...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 33,983
精华内容 13,593
关键字:

vue开发浏览器插件