Vue3에서 나만의 소셜 공유 컴포넌트 작성
다시 작성해야 하는 구성 요소 중 하나는
vue-social-sharing
으로 Vue2에서 사용하기 정말 쉽습니다.소스 코드 해부
vue-social-sharing
의 소스 코드 내부를 파헤치면 세 개의 소스 파일을 찾을 수 있습니다.음, 첫 번째 항목
vue-social-sharing.js
은 올바른 방법으로 구성 요소를 마운트하는 방법을 지시하는 항목 파일입니다.음, 이것은 Vue3에 작성하기 쉽고 예제here도 찾을 수 있습니다.
간단한
Vue.use()
, 이해하기 쉬운, 다른 파일을 확인하자network.js
에서 다음과 같은 여러 소셜 미디어 링크를 내보냅니다.우리는 그것을 유지할 수 있습니다, 아무것도 변경할 필요가 없습니다
이제 남은 문제는
share-network.js
입니다.import AvailableNetworks from './networks'
let $window = typeof window !== 'undefined' ? window : null
export function mockWindow (self) {
$window = self || window // mock window for unit testing
}
export default {
name: 'ShareNetwork',
props: {
/**
* Name of the network to display.
*/
network: {
type: String,
required: true
},
/**
* URL of the content to share.
*/
url: {
type: String,
required: true
},
/**
* Title of the content to share.
*/
title: {
type: String,
required: true
},
/**
* Description of the content to share.
*/
description: {
type: String,
default: ''
},
/**
* Quote content, used for Facebook.
*/
quote: {
type: String,
default: ''
},
/**
* Hashtags, used for Twitter and Facebook.
*/
hashtags: {
type: String,
default: ''
},
/**
* Twitter user, used for Twitter
* @var string
*/
twitterUser: {
type: String,
default: ''
},
/**
* Media to share, used for Pinterest
*/
media: {
type: String,
default: ''
},
/**
* HTML tag used by the Network component.
*/
tag: {
type: String,
default: 'a'
},
/**
* Properties to configure the popup window.
*/
popup: {
type: Object,
default: () => ({
width: 626,
height: 436
})
}
},
data () {
return {
popupTop: 0,
popupLeft: 0,
popupWindow: undefined,
popupInterval: null
}
},
computed: {
/**
* List of available networks
*/
networks () {
return this.$SocialSharing
? this.$SocialSharing.options.networks
: AvailableNetworks
},
/**
* Formatted network name.
*/
key () {
return this.network.toLowerCase()
},
/**
* Network sharing raw sharing link.
*/
rawLink () {
const ua = navigator.userAgent.toLowerCase()
/**
* On IOS, SMS sharing link need a special formatting
* Source: https://weblog.west-wind.com/posts/2013/Oct/09/Prefilling-an-SMS-on-Mobile-Devices-with-the-sms-Uri-Scheme#Body-only
*/
if (this.key === 'sms' && (ua.indexOf('iphone') > -1 || ua.indexOf('ipad') > -1)) {
return this.networks[this.key].replace(':?', ':&')
}
return this.networks[this.key]
},
/**
* Create the url for sharing.
*/
shareLink () {
let link = this.rawLink
/**
* Twitter sharing shouldn't include empty parameter
* Source: https://github.com/nicolasbeauvais/vue-social-sharing/issues/143
*/
if (this.key === 'twitter') {
if (!this.hashtags.length) link = link.replace('&hashtags=@h', '')
if (!this.twitterUser.length) link = link.replace('@tu', '')
}
return link
.replace(/@tu/g, '&via=' + encodeURIComponent(this.twitterUser))
.replace(/@u/g, encodeURIComponent(this.url))
.replace(/@t/g, encodeURIComponent(this.title))
.replace(/@d/g, encodeURIComponent(this.description))
.replace(/@q/g, encodeURIComponent(this.quote))
.replace(/@h/g, this.encodedHashtags)
.replace(/@m/g, encodeURIComponent(this.media))
},
/**
* Encoded hashtags for the current social network.
*/
encodedHashtags () {
if (this.key === 'facebook' && this.hashtags.length) {
return '%23' + this.hashtags.split(',')[0]
}
return this.hashtags
}
},
render: function (createElement) {
if (!this.networks.hasOwnProperty(this.key)) {
throw new Error('Network ' + this.key + ' does not exist')
}
const node = {
class: 'share-network-' + this.key,
on: {
click: () => this[this.rawLink.substring(0, 4) === 'http' ? 'share' : 'touch']()
}
}
if (this.tag === 'a') node.attrs = { href: 'javascript:void(0)' }
return createElement(this.tag, node, this.$slots.default)
},
methods: {
/**
* Center the popup on multi-screens
* http://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen/32261263
*/
resizePopup () {
const width = $window.innerWidth || (document.documentElement.clientWidth || $window.screenX)
const height = $window.innerHeight || (document.documentElement.clientHeight || $window.screenY)
const systemZoom = width / $window.screen.availWidth
this.popupLeft = (width - this.popup.width) / 2 / systemZoom + ($window.screenLeft !== undefined ? $window.screenLeft : $window.screenX)
this.popupTop = (height - this.popup.height) / 2 / systemZoom + ($window.screenTop !== undefined ? $window.screenTop : $window.screenY)
},
/**
* Shares URL in specified network.
*/
share () {
this.resizePopup()
// If a popup window already exist, we close it and trigger a change event.
if (this.popupWindow && this.popupInterval) {
clearInterval(this.popupInterval)
// Force close (for Facebook)
this.popupWindow.close()
this.emit('change')
}
this.popupWindow = $window.open(
this.shareLink,
'sharer-' + this.key,
',height=' + this.popup.height +
',width=' + this.popup.width +
',left=' + this.popupLeft +
',top=' + this.popupTop +
',screenX=' + this.popupLeft +
',screenY=' + this.popupTop
)
// If popup are prevented (AdBlocker, Mobile App context..), popup.window stays undefined and we can't display it
if (!this.popupWindow) return
this.popupWindow.focus()
// Create an interval to detect popup closing event
this.popupInterval = setInterval(() => {
if (!this.popupWindow || this.popupWindow.closed) {
clearInterval(this.popupInterval)
this.popupWindow = null
this.emit('close')
}
}, 500)
this.emit('open')
},
/**
* Touches network and emits click event.
*/
touch () {
window.open(this.shareLink, '_blank')
this.emit('open')
},
emit (name) {
this.$root.$emit('share_network_' + name, this.key, this.url)
this.$emit(name, this.key, this.url)
}
}
}
안에 무엇이 있는지 궁금할 때
this
, 그냥 인쇄해 보겠습니다.여기서는 VueComponent 인스턴스임을 보여줍니다. 메서드, 계산 속성 및 기타 대부분을 먼저 Vue3 스타일로 다시 작성할 수 있습니다. 마지막으로
render
함수를 살펴보겠습니다.고쳐 쓰기
행동 양식
function
또는 화살표 기능변수
ref
또는 reactive
를 사용하여 소품
defineProps
를 사용하여 소품toRefs
또는 toRef
를 사용하세요.방출하다
defineEmits
를 사용하여 방출계산
computed(() => xxx)
를 사용하여 계산된 속성보다
watch(() => props.xxx, (newVal, oldVal) => xxxxxx)
를 사용하여 특정 속성watchEffect
는 우리가 사용할 수 있는 또 다른 것입니다 렌더링 기능
먼저 Vue2 및 Vue3 문서에서 렌더링 함수 항목을 읽어봅시다.
여기에서 Vue2에서 render 함수에 기본 템플릿이 있음을 알 수 있습니다.
Vue2에서:
createElement
는 실제로 함수입니다. 인쇄하여 살펴볼 수 있습니다createElement
createElement
는 3개의 인수를 전달할 수 있습니다.Vue3에는 큰 변화가 있는데, 사실 문서가 변화에 대해 아주 명확하게 설명하고 있는 것 같아서 그냥 다음 부분으로 넘어가겠습니다.
Vue3에서 렌더 로직 재작성
이제 여기에
share-network.js
를 작성해 보겠습니다.import AvailableNetworks from './network';
import {
ref,
computed,
h,
onMounted,
reactive,
toRefs,
getCurrentInstance
} from 'vue';
let $window = typeof window !== 'undefined' ? window : null;
export default {
name: 'DPSharing',
props: {
network: {
type: String,
required: true
},
url: {
type: String,
required: true
},
title: {
type: String,
required: true
},
description: {
type: String,
default: ''
},
quote: {
type: String,
default: ''
},
hashtags: {
type: String,
default: ''
},
twitterUser: {
type: String,
default: ''
},
media: {
type: String,
default: ''
},
tag: {
type: String,
default: 'a'
},
popup: {
type: Object,
default: () => ({
width: 626,
height: 436
})
}
},
emits: ['change', 'close', 'open'],
setup(props, context) {
const { slots, emit } = context;
const popupTop = ref(0);
const popupLeft = ref(0);
const popupWindow = ref(undefined);
const popupInterval = ref(null);
const {
network,
url,
title,
description,
quote,
hashtags,
twitterUser,
media,
tag,
popup
} = toRefs(props);
const networks = computed(() => {
return AvailableNetworks;
});
const key = computed(() => {
return network.value.toLowerCase();
});
const rawLink = computed(() => {
const ua = navigator.userAgent.toLowerCase();
if (
key.value === 'sms' &&
(ua.indexOf('iphone') > -1 || ua.indexOf('ipad') > -1)
) {
return networks.value[key.value].replace(':?', ':&');
}
return networks.value[key.value];
});
const shareLink = computed(() => {
console.log('check sharelink');
let link = rawLink.value;
if (key.value === 'twitter') {
if (!hashtags.value.length) link = link.replace('&hashtags=@h', '');
if (!twitterUser.value.length) link = link.replace('@tu', '');
}
return link
.replace(/@tu/g, '&via=' + encodeURIComponent(twitterUser.value))
.replace(/@u/g, encodeURIComponent(url.value))
.replace(/@t/g, encodeURIComponent(title.value))
.replace(/@d/g, encodeURIComponent(description.value))
.replace(/@q/g, encodeURIComponent(quote.value))
.replace(/@h/g, encodedHashtags.value)
.replace(/@m/g, encodeURIComponent(media));
});
const encodedHashtags = computed(() => {
console.log('check encodedHashtags');
if (key === 'facebook' && hashtags.value.length) {
return '%23' + hashtags.value.split(',')[0];
}
return hashtags.value;
});
const resizePopup = () => {
console.log('resize popup method triggered here ');
const width =
$window.innerWidth ||
document.documentElement.clientWidth ||
$window.screenX;
const height =
$window.innerHeight ||
document.documentElement.clientHeight ||
$window.screenY;
const systemZoom = width / $window.screen.availWidth;
popupLeft.value =
(width - popup.width) / 2 / systemZoom +
($window.screenLeft !== undefined
? $window.screenLeft
: $window.screenX);
popupTop.value =
(height - popup.height) / 2 / systemZoom +
($window.screenTop !== undefined ? $window.screenTop : $window.screenY);
};
const share = () => {
// debugger;
resizePopup();
// If a popup window already exist, we close it and trigger a change event.
if (popupWindow.value) {
clearInterval(popupInterval.value);
// Force close (for Facebook)
popupWindow.value.close();
emit('change');
}
popupWindow.value = $window.open(
shareLink.value,
'sharer-' + key.value,
',height=' +
popup.value.height +
',width=' +
popup.value.width +
',left=' +
popupLeft.value +
',top=' +
popupTop.value +
',screenX=' +
popupLeft.value +
',screenY=' +
popupTop.value
);
// If popup are prevented (AdBlocker, Mobile App context..), popup.window stays undefined and we can't display it
if (!popupWindow.value) return;
popupWindow.value.focus();
// Create an interval to detect popup closing event
popupInterval.value = setInterval(() => {
if (!popupWindow.value || popupWindow.value.closed) {
clearInterval(popupInterval.value);
popupWindow.value = null;
emit('close');
}
}, 500);
emit('open');
};
const touch = () => {
console.log('touch method triggered');
window.open(shareLink.value, '_blank');
emit('open');
};
const renderData = () => {
if (!networks.value.hasOwnProperty(key.value)) {
throw new Error('Network ' + key.value + ' does not exist');
}
const node = {
class: 'share-network-' + key.value,
on: {
click: rawLink.value.substring(0, 4) === 'http' ? share : touch
},
attrs: {
href:
rawLink.value.substring(0, 4) === 'http'
? shareLink.value
: rawLink.value
}
};
if (tag.value === 'a') {
node.attrs = { href: 'javascript:void(0)' };
}
return [tag.value, node, slots.default()];
};
const data = renderData();
const tg = `${data[0]}`;
const node = data[1];
const content = data[2];
return () =>
h(
tg,
{
onClick: node.on.click,
href: node.attrs.href
},
content
);
}
};
마지막에 반환하는 렌더링 함수에 주의를 기울이십시오. 먼저 렌더링 목적으로 데이터를 반환하는
renderData
라는 함수를 식별하고 console.log()
응답을 표시합니다.{
"a",
{
"class": "share-network-facebook",
"on": {},
"attrs": {
"href": "javascript:void(0)"
}
}
}
a
태그는이를 내보내고 사용할 구성 요소를 등록할 수 있습니다.
예시:
<COSharing network="facebook" :title="'facebook'" url="https://www.google.com" quote="hello" >
click me to open whatsapp sharing
</COSharing>
quote
트위터용요소를 검사하면
a
태그로 래핑된 요소를 찾을 수 있습니다.<a href="javascript:void(0)" data-v-5be083fa="">
// whatever your content here
</a>
이제 우리는 Vue3 for
vue-social-sharing
라이브러리에 성공적으로 재작성했습니다. 더 이상 해당 패키지의 업데이트에 의존할 필요가 없습니다. 자신만의 패키지를 만드는 방법을 배울 수 있습니다. 이는 실제로 좋은 습관입니다.
Reference
이 문제에 관하여(Vue3에서 나만의 소셜 공유 컴포넌트 작성), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/warriorjar/write-your-own-social-sharing-component-in-vue3-2imn텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)