從頭開始一步一步將 youtube 筆記功能做一下整理
由於有使用 vue ,所以下載 Vue 的範本使用 :
$ npm install -g vue-cli
$ vue init YuraDev/vue-chrome-extension-template my-project
$ cd my-project
$ npm install
$ npm run dev
該範本有使用 eslint ,如果要避免嚴格規則的錯誤,可以到 core\webpack.base.js 再 rule 底下註解掉 :
下載 JQuery 檔案,並放到 src 的 lib 資料夾
在 core 找到 webpack.base.js 的 entry 添加 jquery : resolve('./lib/jquery-3.3.1.min.js')
  entry: {
    tab: resolve('./tab'),
    popup: resolve('./popup'),
    options: resolve('./options'),
    content: resolve('./content'), 
    devtools: resolve('./devtools'),
    background: resolve('./backend'),
    panel: resolve('./devtools/panel'),
    inject: resolve('./content/inject'),
    jquery : resolve('./lib/jquery-3.3.1.min.js'),
  },
manifest.js 的 content_scripts 修正為 :
  content_scripts: [{
    js: [ 'js/jquery.js','js/content.js' ],
    run_at: 'document_end',
    matches: ['https://www.youtube.com/*'],
    all_frames: true
  }],
修改 content\index.js
import Vue from 'vue'
import root from './content.vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.config.productionTip = false
Vue.use(ElementUI)
var vueLoop = setInterval(function() {
  if($('#related').length > 0 && $('#items').length){
    $('<div id="root"></div').prependTo( "#related");
    new Vue({
      el: '#root',
      render: h => h(root)
    });
    clearTimeout(vueLoop);
  }
  }, 500);
在 content 資料夾底下建立 content.vue
content.vue 內容 :
其他備註 - 資料儲存發現無法使用變數來存,導致資料存取有問題,所以採取全讀的方式 :
var myName ="helloData";
chrome.storage.sync.set({ myName: "123" }, function() {});
儲存的 key 會是 myName 而不是 helloData
chrome.storage.sync.get("helloData", function(result) {
    console.log(result);
})
chrome.storage.sync.get("myName", function(result) {
    console.log(result);
})

其他備註 - 點選其他影片時不會重載網頁,所以使用迴圈檢查影片參數(v) :
        showTime(){
            var vm = this;
            (function() {
                let animationTimer = setInterval(function() {
                    var videoKey = vm.getParameter('v');
                    if (videoKey != vm.videoKey) {
                        vm.videoKey = videoKey;
                        vm.items = [];
                        vm.loadData();
                    }
                    var video = document.getElementsByClassName('video-stream')[0];
                    vm.isPaused = video.paused;
                    var t = Math.floor(video.currentTime);
                    if (vm.currentTime !== t) {
                        vm.currentTime = t;
                    }
                }, 500);
            })();
        }
其他備註 - youtube 影片使用 Html5 Video 所以可以用它來操作影片相關功能 :
var video = document.getElementsByClassName('video-stream')[0];
video.currentTime = time; //指定時間
video.pause(); //暫停
video.play(); //播放
video.paused; //狀態,是否暫停
content.vue 的 templdate 部分 :
<template>
    <el-container>
        <el-main>
            <el-row v-show="!isPaused">
                <el-col>
                    <el-button type="primary" @click="play()">輸入訊息</el-button>
                </el-col>
            </el-row>
            <el-row v-show="isPaused">
                <el-col>
                    <el-button type="success" @click="addData()">儲存</el-button>
                    <el-button type="primary" @click="play()">繼續撥放</el-button>
                </el-col>
            </el-row>
            <el-row v-show="isPaused">
                <el-col>
                    <el-input type="textarea" :rows="2" placeholder="请输入内容" v-model="currentMessage"></el-input>
                </el-col>
            </el-row>
            <el-row>
                <el-col :span="16">
                    <ul v-for="(item,index) in items">
                        <li>
                          <el-button type="danger" size="mini" icon="el-icon-delete" circle @click=deleteItem(index)></el-button>
                          <el-button size="mini" @click="gotoTime(item.time)">{{getMinSec(item.time)}} - {{item.message}}</el-button> 
                        </li>
                    </ul>
                </el-col>
            </el-row>
        </el-main>
    </el-container>
</template>
content.vue 的 script 部分 :
<script>
export default {
    data: () => ({
        currentTime: 0,
        currentMessage: '',
        items: [],
        videoKey: '',
        isPaused: false
    }),
    computed: {},
    created() {},
    mounted() {
        this.videoKey = this.getParameter('v');
        this.showTime();
        this.loadData();
    },
    updated() {
    },
    computed: {
    },
    methods: {
        play() {
            document.querySelector('.ytp-play-button').click();
        },
        getMinSec(t) {
            return Math.floor(t / 60) + '分' + (t % 60) + '秒';
        },
        gotoTime(time){
            var video = document.getElementsByClassName('video-stream')[0];
            video.currentTime = time;
            video.pause();
        },
        showTime(){
            var vm = this;
            (function() {
                let animationTimer = setInterval(function() {
                    var videoKey = vm.getParameter('v');
                    if (videoKey != vm.videoKey) {
                        vm.videoKey = videoKey;
                        vm.items = [];
                        vm.loadData();
                    }
                    var video = document.getElementsByClassName('video-stream')[0];
                    vm.isPaused = video.paused;
                    var t = Math.floor(video.currentTime);
                    if (vm.currentTime !== t) {
                        vm.currentTime = t;
                    }
                }, 500);
            })();
        },
        loadData() {
            var vm = this;
            chrome.storage.sync.get("tempData", function(result) {
                if (result !== undefined) {
                    var tempData = result.tempData;
                    var jsonResult = [];
                    if (tempData !== undefined) {
                        jsonResult = tempData;
                    }
                    jsonResult.forEach(function(video) {
                        if (video.videoKey == vm.videoKey) {
                            vm.items = video.items;
                        }
                    })
                }
                if (vm.items === undefined) {
                    vm.items = [];
                }
            });
        },
        addData() {
            var vm = this;
            var filterByTime = vm.items.filter(function(item, index, array) {
                return item.time == vm.currentTime;
            });
            if (filterByTime.length > 0) {
                vm.items.forEach(function(item, index, array) {
                    if (item.time == vm.currentTime) {
                        item.message = vm.currentMessage;
                    }
                });
            } else {
                var item = {
                    time: vm.currentTime,
                    message: vm.currentMessage
                }
                vm.items.push(item);
            }
            vm.saveData();
        },
        saveData(){
            var vm = this;
            chrome.storage.sync.get("tempData", function(result) {
                var tempData = result.tempData;
                var jsonResult = [];
                if (tempData !== undefined) {
                    jsonResult = tempData;
                }
                var isExist = false;
                jsonResult.forEach(function(video) {
                    if (video.videoKey == vm.videoKey) {
                         video.items = vm.items;
                        isExist = true;
                    }
                });
                if (!isExist) {
                    var videoItem = {
                        videoKey: vm.videoKey,
                        items: vm.items
                    };
                    jsonResult.push(videoItem);
                }
                chrome.storage.sync.set({ "tempData": jsonResult }, function() {});
            });
        },
        deleteItem(index){
             var vm = this;
             vm.items.splice(index,1);
             vm.saveData();
        },
        getParameter(name, url) {
            if (!url) url = window.location.href;
            name = name.replace(/[\[\]]/g, "\\$&");
            var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
                results = regex.exec(url);
            if (!results) return null;
            if (!results[2]) return '';
            return decodeURIComponent(results[2].replace(/\+/g, " "));
        }
    },
    watch: {
        currentTime: function(newValue, oldValue) {
            var vm = this;
            var filterA = vm.items.filter(function(item, index, array) {
                return item.time == newValue;
            });
            if (filterA.length > 0) {
                vm.currentMessage = filterA[0].message;
            } else {
                vm.currentMessage = '';
            }
        }
    }
}
</script>
執行結果 - 最終完成可以在網頁上增加類似影片標籤的功能 :

感謝收看 :)