指令
<div v-on:click="myClick"></div>
<div v-style="myStyle"></div>
<div v-show="display"></div>
<div v-show:display></div>
<div v-class="myClass"></div>
<div v-model="myModel"></div>
<div v-ref="myRef"></div>
<div v-text="myText"></div>
<div v-html="myHTML"></div>
全局
const state=observe({
count:GM_getValue('count',1),
})
new Watcher(null,()=>{
return state.count
},(newVal)=>{
GM_setValue('count',newVal)
})
const Counter = {
template: `
<button v-on:click="decrease">-</button>
<span>{{global.count}}</span>
<button v-on:click="increase">+</button>
`,
data(){
return {
global:state,
}
},
methods:{
increase(){
this.global.count+=1
},
decrease(){
this.global.count-=1
},
}
}
组件
const MyTitle = {
data() {
return {
title: '标题',
}
},
template: `
<div class="test">
<h1 v-ref="title1"> {{ title }}1 </h2>
<h2> {{ title }}2 </h2>
<slot></slot>
<label>
标题:
<input v-model="title" type="text" placeholder="修改标题" />
</label>
</div>
`
}
const MyFooter = {
data() {
return {
title: '尾部',
}
},
template: `
<div class="test">
<h1 v-ref="title1"> {{ title }} </h2>
</div>
`,
mounted() {
this.$refs.title1.textContent = '尾部:ref取element改变文字'
console.log('before nextTick')
this.$nextTick().then(() => {
console.log('nextTick(micro)')
})
console.log('after nextTick')
}
}
const MyVM = {
element: '#app',
components: {
'my-title': MyTitle,
'my-footer': MyFooter
},
data: {
number: {
input: 0,
click: 0
},
p: '这里是内容',
display: true,
tempObject: {},
fontSizeStyle: { 'font-size': '14px' },
inputNumberOver100Times: 0,
numberInputClass: 'red',
inputClassesIndex: 0,
inputClasses: ['red', 'blue', 'yellow', 'grey', 'gold', 'black', 'white']
},
computed: {
getInformation() {
return `点击次数:${this.number.click} 增量:${this.number.input}`
},
getInputNumberOver100TimesInformation() {
return `增量超过100的次数:${this.inputNumberOver100Times}`
},
getNewObject() {
const a = this.tempObject.test
return a ? `${a.name}` : a
}
},
watch: {
"number.input": function (newVal, oldVal) {
if (newVal >= 100 && oldVal < 100)
this.inputNumberOver100Times += 1
},
"number.click": function (newVal, oldVal) {
this.fontSizeStyle = {
'font-size': 14 + newVal + 'px'
}
},
"inputNumberOver100Times": function () {
this.$emit('over100Times')
}
},
methods: {
setNewObject(e) {
this.tempObject.test = {
name: 'test'
}
console.log('create test', this.tempObject)
},
deleteNewObject(e) {
if ('test' in this.tempObject) {
delete this.tempObject.test
}
console.log('delete test', this.tempObject)
},
displayController(e) {
this.display = !this.display
},
increaseClickNumber() {
this.number.click += 1
this.$emit('click')
},
increase1(e) {
this.number.input += 1
this.increaseClickNumber()
},
increase10(e) {
this.number.input += 10
this.increaseClickNumber()
},
decrease10(e) {
this.number.input -= 10
this.increaseClickNumber()
},
switchInputTextColor(e) {
this.inputClassesIndex += 1
if (this.inputClassesIndex === this.inputClasses.length)
this.inputClassesIndex = 0
this.numberInputClass = this.inputClasses[this.inputClassesIndex]
this.increaseClickNumber()
}
}
}
const vm = createVM(MyVM);
vm.$on('click', function () {
console.log('clickNumber:', this.number.click)
})
vm.$once('over100Times', function () {
console.log('over 100!')
})
const cVM = createComponent(MyTitle)
cVM.$mount('#app2')
用例
const TIME_OUT = 60 * 1000
const FILTER_URLS = [
'https://static.myfigurecollection.net/ressources/nsfw.png',
'https://static.myfigurecollection.net/ressources/spoiler.png'
]
const globalState =observe({
group:GM_getValue('groupCount',1),
count:GM_getValue('picCount',1),
componentDownloadStatus:{},
downloadStates:{
normal:0,
loading:0,
error:0,
timeout:0,
downloaded:0,
}
})
new Watcher(null,()=>{
return globalState.count
},(newVal)=>{
GM_setValue('picCount',newVal)
})
new Watcher(null,()=>{
return globalState.group
},(newVal)=>{
GM_setValue('groupCount',newVal)
globalState.count = 0
})
new Watcher(null,()=>{
return globalState.componentDownloadStatus
},(newVal)=>{
Object.assign(globalState.downloadStates,{
normal:0,
loading:0,
error:0,
timeout:0,
downloaded:0,
})
const states = globalState.downloadStates
Object.keys(newVal).forEach(key=>{
const status = newVal[key]
if(states[status] != null)
states[status]+=1
})
},true)
function beforeDownload(){
if(GM_getValue('groupCount',1) != globalState.group){
const curCount = GM_getValue('picCount',1) + 1
globalState.group = GM_getValue('groupCount',1)
globalState.count = curCount
}else{
globalState.group = GM_getValue('groupCount',1)
globalState.count = GM_getValue('picCount',1) + 1
}
return {group:globalState.group,count:globalState.count}
}
const DownloadSequence = {
template: `
<span>当前组:</span>
<button v-on:click="decreaseGroup">-</button>
<span>{{global.group}}</span>
<button v-on:click="increaseGroup">+</button>
<span>当前图片:</span>
<button v-on:click="decreaseCount">-</button>
<span>{{global.count}}</span>
<button v-on:click="increaseCount">+</button>
`,
data(){
return {
global:globalState,
}
},
methods:{
increaseCount(){
this.global.count+=1
},
decreaseCount(){
this.global.count-=1
},
increaseGroup(){
this.global.group+=1
},
decreaseGroup(){
this.global.group-=1
}
}
}
const REQEUST_BUTTON_STYLES = {
normal:{},
loading: { background: 'white',color:'black',cursor:'wait'},
error:{background:'red',color:'white'},
timeout:{background:'yellow',color:'black'},
downloaded: {background:'green',color:'white'}
}
const DownloadButton = {
template: `
<button v-on:click="download" v-style="downloadBtnStyle">
{{downloadedMsg}}
</button>
`,
data(){
return {
oldStatus:'normal',
downloadStatus:'normal' // 'normal' 'loading' 'error' 'timeout' 'downloaded'
}
},
computed:{
downloadBtnStyle(){
return REQEUST_BUTTON_STYLES[this.downloadStatus]
},
downloadedMsg(){
const messages = {
normal:'下载原图' ,
loading: '正在下载...',
error:'下载失败',
timeout:'下载超时',
downloaded: '重新下载'
}
return messages[this.downloadStatus]
}
},
watch:{
downloadStatus(newStatus,oldStatus){
this.oldStatus = oldStatus
globalState.componentDownloadStatus[this.cid] = newStatus
}
},
created(){
globalState.componentDownloadStatus[this.cid] = this.downloadStatus
},
destoryed(){
delete globalState.componentDownloadStatus[this.cid]
},
methods:{
download(){
if(this.downloadStatus === 'loading') return
this.downloadStatus = 'loading'
if(this.oldStatus !== 'error' && this.oldStatus !== 'timeout')
this.value = beforeDownload()
this.$emit('download',this.value)
},
}
}
const DownloadState = {
template:`
<div style="display:flex;flex-direction:row;padding:5px">
<div style="margin-right:5px;color:grey">
<span style="">未下载:</span>
<span>{{states.normal}}</span>
</div>
<div style="margin-right:5px;color:green">
<span style="">已下载:</span>
<span>{{states.downloaded}}</span>
</div>
<div style="margin-right:5px;color:black">
<span style="">正在下载:</span>
<span>{{states.loading}}</span>
</div>
<div style="margin-right:5px;color:brown">
<span style="">下载超时:</span>
<span>{{states.timeout}}</span>
</div>
<div style="color:red">
<span>下载报错:</span>
<span>{{states.error}}</span>
</div>
</div>
`,
data(){
return {
states:globalState.downloadStates
}
},
methods:{
download(value){
this.$emit('download',value)
}
}
}
const PictureDownload = {
components:{
'download-button':DownloadButton,
'download-sequence':DownloadSequence,
},
template:`
<div>
<download-sequence></download-sequence>
<span v-show:downloaded style="margin:0 10px">{{msg}}</span>
<download-button v-ref="downloadButton" v-on:download="download"></download-button>
</div>
`,
data(){
return {
group:0,
count:0,
downloaded:false
}
},
computed:{
msg(){
return `${this.group}.${this.count}`
}
},
mounted(){
const value = GM_getValue(window.location.href)
if(!value) return
this.$refs.downloadButton.downloadStatus = value.downloadStatus
this.$refs.downloadButton.value = {
group: value.group,
count: value.count
}
this.downloaded = true
this.group = value.group
this.count = value.count
},
methods:{
download(value){
this.downloaded = true
this.group = value.group
this.count = value.count
this.$emit('download',value)
}
}
}
function insertAfter(targetNode, afterNode){
const parentNode = afterNode.parentNode
const beforeNode = afterNode.nextElementSibling
if(beforeNode == null)
parentNode.appendChild(targetNode)
else
parentNode.insertBefore(targetNode, beforeNode)
}
function mountVM(node,component){
const vm = new MVVMComponent(component)
vm.$mount(node)
return vm
}
function download({vm,picture,group,count,origin}){
const values = origin.split('/')
const fileType = values[values.length - 1].split('.')[1]
const name = `${group}.${count}.${fileType}`
const end = (status)=>{
return ()=>{
vm.downloadStatus = status
GM_setValue(picture,{origin,group,count,downloadStatus:status})
}
}
GM_download({
url:origin,
name,
timeout:TIME_OUT,
onload:end('downloaded'),
onerror:end('error'),
ontimeout:end('timeout'),
})
}
function renderPictureExtension(objectMeta,thePicture){
const origin = thePicture.href
const div = document.createElement('div')
objectMeta.appendChild(div)
const vm = mountVM(div,PictureDownload)
vm.$on('download',(value)=>{
download({
...value,
origin,
picture:window.location.href,
vm:vm.$refs.downloadButton,
})
})
}
const createPictruePreview = ({thumb,origin,picture}) => {
const commonStyle={
'margin':'10px 10px',
'border':'1px solid #000',
'padding':'10px',
'border-radius':'5px',
'background':'#fff',
'transition':'all 0.5s',
'box-sizing': 'border-box'
}
return {
components:{
'download-button':DownloadButton
},
template: `
<div v-style="containerStyle">
<div style="margin-bottom:10px;display:flex;flex-direction:row;justify-content:center;align-items:center;width:100%;">
<div style="margin:0 10px;flex-shrink:0"><img style="cursor:pointer" v-on:click="openPicturePage" [src]="thumb" /></div>
<div style="flex-shrink:1" v-show="originalImage"><img style="width:100%" [src]="src" /></div>
</div>
<div style="display:flex;justify-content:center;align-items:center;flex-direction:column">
<download-button v-ref="downloadButton" v-on:download="download"></download-button>
<br v-show:downloaded />
<div v-show:downloaded>
<span>组:</span>
<span >{{group}}</span>
<span>图片:</span>
<span >{{count}}</span>
</div>
<br />
<button v-on:click="toggle">{{msg}}</button>
<br />
<button v-show:refresh v-on:click="refreshOrigin" v-style="refreshBtnStyle">
{{refreshMsg}}
</button>
</div>
</div>
`,
data(){
return {
thumb,
origin,
picture,
refresh:FILTER_URLS.includes(origin),
originalImage:false,
group:0,
count:0,
downloaded:false,
refreshStatus:'normal' // 'normal' 'loading' 'error' 'timeout' 'downloaded'
}
},
computed:{
src(){
return this.originalImage ? this.origin : this.thumb
},
msg(){
return this.originalImage ? '关闭预览' : '预览原图'
},
containerStyle(){
return Object.assign({},commonStyle,this.originalImage ? {
width:'100%'
}:{} )
},
refreshBtnStyle(){
return REQEUST_BUTTON_STYLES[this.refreshStatus]
},
refreshMsg(){
const messages = {
normal:'获取隐藏图片' ,
loading: '正在获取...',
error:'获取失败',
timeout:'获取超时',
downloaded: '重新获取'
}
return messages[this.refreshStatus]
}
},
mounted(){
const value = GM_getValue(picture)
if(!value) return
this.$refs.downloadButton.downloadStatus = value.downloadStatus
this.$refs.downloadButton.value = {
group: value.group,
count: value.count
}
this.downloaded = true
this.group = value.group
this.count = value.count
},
methods:{
download(value){
this.downloaded = true
this.group = value.group
this.count = value.count
if(this.refresh && this.refreshStatus !== 'downloaded')
this.refreshOrigin()
.then(()=>{
this.$emit('download',{...value,origin:this.origin})
})
else
this.$emit('download',{...value,origin:this.origin})
},
toggle(){
if(this.refresh && !this.originalImage && this.refreshStatus !== 'downloaded')
this.refreshOrigin()
this.originalImage = !this.originalImage
},
refreshOrigin(){
if(this.refreshStatus === 'loading') return
this.refreshStatus = 'loading'
return new Promise((resolve,reject)=>{
GM_xmlhttpRequest({
url:this.picture,
responseType:'document',
timeout:TIME_OUT,
onload:(data)=>{
const doc = data.response
const a = doc.querySelector('.the-picture>a')
if(a){
this.origin = a.href
const thumb = a.href.split('/')
thumb.splice(thumb.length - 1,0,'thumbnails')
this.thumb = thumb.join('/')
this.refreshStatus = 'downloaded'
resolve()
return
}
this.refreshStatus = 'error'
reject()
},
onerror:()=>{
this.refreshStatus = 'error'
reject()
},
ontimeout:()=>{
this.refreshStatus = 'timeout'
reject()
}
})
})
},
openPicturePage(){
window.open(picture)
}
}
}
}
function parseOriginalImageURL(thumb_url){
if(thumb_url.indexOf('thumbnails/') > -1){
url = thumb_url.split('thumbnails/').join('')
} else {
const paths = thumb_url.split('pictures/')[1].split('/')
if(paths.length > 2){
paths.splice(3,1)
url = [thumb_url.split('pictures/')[0],paths.join('/')].join('pictures/')
}
}
return url
}
function getImageURLs(node){
const picture = node.querySelector('a').href
const viewport = node.querySelector('.viewport')
const thumb = viewport.style.background.split('"')[1]
let origin = thumb
if(FILTER_URLS.includes(origin))
origin = thumb
else
origin = parseOriginalImageURL(origin)
return {thumb,origin,picture}
}
function renderPicturesExtension(thumbs){
function _initParentNode(parentNode){
parentNode.innerHTML = ''
parentNode.style.setProperty('display','flex')
parentNode.style.setProperty('flex-direction','row')
parentNode.style.setProperty('flex-wrap','wrap')
parentNode.style.setProperty('justify-content','center')
parentNode.style.setProperty('align-items','center')
const div1 = document.createElement('div')
parentNode.parentNode.insertBefore(div1,parentNode)
mountVM(div1,DownloadSequence)
const div2 = document.createElement('div')
const pageCount = document.querySelector('.listing-count-pages') || parentNode
pageCount.parentNode.insertBefore(div2,pageCount)
mountVM(div2,DownloadState)
}
const parentNode = thumbs[0].parentNode
_initParentNode(parentNode)
thumbs.forEach(thumb_node=>{
const imageURLs = getImageURLs(thumb_node)
const preview = createPictruePreview(imageURLs)
const div3 = document.createElement('div')
parentNode.appendChild(div3)
const previewVM = mountVM(div3,preview)
previewVM.$on('download',(value)=>{
download({
...value,
picture:imageURLs.picture,
vm:previewVM.$refs.downloadButton,
})
})
})
}
function render(){
const objectMeta = document.querySelector('.object-meta')
const thePicture = document.querySelector('.the-picture>a')
if(objectMeta && thePicture)
renderPictureExtension(objectMeta,thePicture)
const thumbs = []
document.querySelectorAll('.picture-icon.tbx-tooltip').forEach(thumb=>{
thumbs.push(thumb)
})
if(thumbs.length > 0)
renderPicturesExtension(thumbs.reverse())
}
render()